if (tactile == null || typeof(tactile) != "object") {
	var tactile = {};
	tactile.version = "1.1-201208091645";
	tactile.pageLayout = false;
}
if (tactile.c == null || typeof (tactile.c) != "object") {
	tactile.c = {};
}
if(typeof Object.getOwnPropertyNames !== "function") {
	Object.getOwnPropertyNames = function (o) {
		if (o !== Object(o)) { throw new TypeError("Object.getOwnPropertyNames called on non-object"); }
		var props = [], p;
		for (p in o) {
			if (Object.prototype.hasOwnProperty.call(o, p)) {
				props.push(p);
			}
		}
		return props;
	};
};
if(!Array.prototype.indexOf){
	Array.prototype.indexOf = function(el, start) {
	    var i, s = start || 0;
	    for ( i=s; i < this.length; ++i ) {
	        if ( this[i] === el ) {
	            return i;
	        }
	    }
	    return -1;
	};
};
if(!Array.prototype.contains){
	Array.prototype.contains = function(elem){
	for (var i = 0; i < this.length; i++) {
		if (this[i] === elem) {
			return true;
		}
	}
	return false;
	};
};
if(!Array.prototype.forEach){
	Array.prototype.forEach = function(fn, thisObj) {
	    var i, scope = thisObj || window;
	    for ( i=0, j=this.length; i < j; ++i ) {
	        fn.call(scope, this[i], i, this);
	    }
	};
};
if (!Array.prototype.every) {
	Array.prototype.every = function(fn, thisObj) {
	    var i, scope = thisObj || window;
	    for ( i=0, j=this.length; i < j; ++i ) {
	        if ( !fn.call(scope, this[i], i, this) ) {
	            return false;
	        }
	    }
	    return true;
	};
};
if (!Array.prototype.some) {
	Array.prototype.some = function(fn, thisObj) {
	    var i, scope = thisObj || window;
	    for ( i=0, j=this.length; i < j; ++i ) {
	        if ( fn.call(scope, this[i], i, this) ) {
	            return true;
	        }
	    }
	    return false;
	};
};
if (!Array.prototype.map) {
	Array.prototype.map = function(fn, thisObj) {
	    var i, a = [], scope = thisObj || window;
	    for ( i=0, j=this.length; i < j; ++i ) {
	        a.push(fn.call(scope, this[i], i, this));
	    }
	    return a;
	};
};
if(!Array.prototype.filter) {
	Array.prototype.filter = function(fn, thisObj) {
	    var i, j=this.length, a = [], scope = thisObj || window;
	    for(i=0; i < j; ++i ) {
	        if(!fn.call(scope, this[i], i, this)) {
	            continue;
	        }
	        a.push(this[i]);
	    }
	    return a;
	};
};
if (!Array.prototype.lastIndexOf) {
	Array.prototype.lastIndexOf = function(el, start) {
	    var i, s = start || this.length;
	    if ( s >= this.length ) {
	        s = this.length;
	    }
	    if ( s < 0 ) {
	         s = this.length + start;
	    }
	    for ( i=s; i >= 0; --i ) {
	        if ( this[i] === el ) {
	            return i;
	        }
	    }
	    return -1;
	};
};
if (!Array.prototype.remove) {
	Array.prototype.remove = function(el) {
		var ind = this.indexOf(el);
		if(ind != -1){
			this.splice(ind, 1);
		}
	};
};
document.getElementsByAttribute = function( attr, value, context_node, tag ) {
    var nodes = [];
    if ( context_node == null )
        context_node = this;
    if ( tag == null )
        tag = '*';
    var elems = context_node.getElementsByTagName(tag);
    for ( var i = 0; i < elems.length; i++ ) {
    	if(elems[i].hasAttribute(attr)){
    		if(value){
    			if(elems[i].getAttribute(attr) == value){
    				nodes.push(elems[i]);
    			}
    		}else{
    			nodes.push(elems[i]);
    		}
    	}
    }
    return nodes;
};
document.getFirstBodyChild = function() {
	var b = document.body;
	if (b.firstElementChild) {
		return b.firstElementChild;
	}
	if (b.firstChild && b.firstChild.nodeType === 1) {
		return b.firstChild;
	}
	return undefined;
};
if(!document.getElementsByClassName){
	document.getElementsByClassName = function(className){
		return tactile.c.E.getElementsByClassName(document,className);
	};
}
document.getAll = function(classesAndIds,refNode) {
	var result = new Array();
	var params = classesAndIds.split(",");
	var classes, ids, elems;
	var separator = "";
	classes = "";
	var ref = refNode || document;
	for ( var i = 0; i < params.length; i++) {
		if (params[i].indexOf(".") == 0) {
			elems = new Array();
			if (ref.querySelectorAll) {
				elems = ref.querySelectorAll(params[i]);
				for ( var k = 0; k < elems.length; k++) {
					result.push(elems[k]);
				}
			} else {
				if (classes != "") {
					separator = ",";
				}
				classes += separator + params[i].substr(1);
			}
		} else if (params[i].indexOf("#") == 0) {
			elems = document.getElementById(params[i].substr(1));
			if (elems && elems != null) {
				result.push(elems);
			}
		}
	}
	if (classes != "") {
		elems = tactile.c.E.getElementsByClassName(ref,classes);
		if (elems && elems.length > 0) {
			for ( var j = 0; j < elems.length; j++) {
				result.push(elems[j]);
			}
		}
	}
	return result;
};
Function.prototype.bind = function(object) {
	var __method = this,
    rFunc = function() {
        return __method.apply(object, arguments);
    };
    rFunc.toString = function(){ return __method.toString(); }
    return rFunc;
};
Function.prototype.extend = function(object){
	if(typeof object === 'object'){
		for(var key in object){
			if(object.hasOwnProperty(key)){
				this.prototype[key] = object[key];
			}
		}
	}
}
if (!String.prototype.ltrim){
	String.prototype.ltrim = function (substring) {
		substring = substring || "\\s";
	    return this.replace(new RegExp("^[" + substring + "]+", "g"), "");
	}
}
if (!String.prototype.rtrim){
	String.prototype.rtrim = function (substring) {
		substring = substring || "\\s";
	    return this.replace(new RegExp("[" + substring + "]+$", "g"), "");
	}
}
if (!String.prototype.trim){
	String.prototype.trim = function (substring) {
	    return this.ltrim(substring).rtrim(substring);
	};
}
if (!String.prototype.capitalize){
	String.prototype.capitalize = function(){
	    return this.replace( /(^|\s)([a-z])/g , function(m,p1,p2){
	    	return p1+p2.toUpperCase();
	    });
	};
}
tactile.c.AppInfo = {
		internalPath : (function(){
			var appPath, internalPath;
			if (typeof nbdevice === "object") {
				if (nbdevice.internalapppath) {
					appPath = nbdevice.internalapppath;
				}
				if (nbdevice.internalpath) {
					internalPath = nbdevice.internalpath;
				}
				if (internalPath === undefined || internalPath === null	|| internalPath.length === 0) {
					internalPath = "nbinternal";
				}
				if (!(internalPath.indexOf("http") === 0)) {
					internalPath = appPath + internalPath;
				}
			}
			return internalPath;
		})()
}
tactile.c.Error = function(message){
	this.name = "tactile.c.Error";
	this.message = "A error has occured !";
	this.number = 900;
}
tactile.c.C = {};
tactile.c.C.getCookie = function(cookieName) {
	if (document.cookie.length > 0) {
		var cookieStart = document.cookie.indexOf(cookieName + "=");
		if (cookieStart !== -1) {
			cookieStart = cookieStart + cookieName.length + 1;
			var cookieEnd = document.cookie.indexOf(";", cookieStart);
			if (cookieEnd === -1) {
				cookieEnd = document.cookie.length;
			}
			return unescape(document.cookie.substring(cookieStart, cookieEnd));
		}
	}
	return "";
};
tactile.c.C.setCookie = function(cookieName, cookieValue, expiredays) {
	var exdate = new Date();
	exdate.setDate(exdate.getDate() + expiredays);
	document.cookie = cookieName + "=" + escape(cookieValue)
			+ ((expiredays === null) ? "" : ";expires=" + exdate.toGMTString());
};
tactile.c.C.deleteCookie = function(cookieName) {
	if (this.getCookie(cookieName)) {
		document.cookie = cookieName
				+ '=; expires=Thu, 01-Jan-70 00:00:01 GMT;';
	}
};
tactile.c.D = {
	type: ((typeof nbdevice === "object") && nbdevice.devicetype) ? nbdevice.devicetype : "",
	displayCategory: ((typeof nbdevice === "object") && nbdevice.tactiledisplaycategory) ? nbdevice.tactiledisplaycategory : "",
	os: (function(){
		var res = {};
		res.platform = 'unknown';
		res.version = 0;
		res.isTouch = istouchdevice();
		if (window.navigator) {
			var uac = [ {
				id : 'android',
				ver : 'android[/|\\s]([0-9].[0-9])'
			}, {
				id : 'bada',
				ver : 'bada/([0-9])'
			}, {
				id : 'webos',
				ver : 'webos/([0-9].[0-9])'
			}, {
				id : 'blackberry',
				ver : 'blackberry[0-9]{4}?/([0-9].[0-9])'
			}  ];
			var upc = [ {
				id : 'iphone',
				ver : 'iphone os ([0-9])'
			}, {
				id : 'ipod',
				ver : 'os ([0-9])'
			}, {
				id : 'ipad',
				ver : 'os ([0-9])'
			}, {
				id : 'mac',
				ver : null
			}, {
				id : 'win',
				ver : null
			} ];
			var ua = window.navigator.userAgent.toLowerCase();
			for ( var i = 0; i < uac.length; i++) {
				if (ua.indexOf(uac[i].id) != -1) {
					res.platform = uac[i].id;
					if (uac[i].ver !== null && ua.match(RegExp(uac[i].ver)) != -1) {
						res.version = RegExp.$1;
					}
					break;
				}
			}
			if (res.platform == "unknown") {
				var up = window.navigator.platform.toLowerCase();
				for ( var j = 0; j < upc.length; j++) {
					if (up.indexOf(upc[j].id) != -1) {
						if ( upc[j].id === 'iphone' || upc[j].id === 'ipod' || upc[j].id === 'ipad')
						res.platform = "ios";
						if (upc[j].ver !== null	&& ua.match(RegExp(upc[j].ver)) != -1) {
							res.version = RegExp.$1;
						}
						break;
					}
				}
				if (res.platform == "unknown") {
					res.platform = up;
				}
			}
		}
		function istouchdevice() {
			if(typeof nbdevice === "object" && nbdevice.tactiletouchapi){
				return (nbdevice.tactiletouchapi == "Touch");
			}
			try {
				document.createEvent("TouchEvent");
			    return true;
			 } catch (e) {
				 return false;
			 }
		}
		return res;
	})(),
	browser:(function() {
		var res = {},
		features = {},
		t = document.createElement('feature'),
		ts = t.style,
	    cssprefixes = {
			webkit: "-webkit-",
			opera: "-o-",
			netfront: "",
			firefox: "-moz-",
			iemobile: "-ms-",
			msie: "-ms"
		}
		cssborderradius = 'cssborderradius'
		cssanimations = 'cssanimations',
		csstransitions = 'csstransitions',
		csstransforms = 'csstransforms',
		csstransforms3d = 'csstransforms3d';
		res.style = {};
		res.cssprefix = "";
		res.name = 'unknown';
		res.transitionEndEvent = "";
		var emvcc = tactile.c.C.getCookie("emvcc");
		res.canCookie = (emvcc == "") ? false : true;
		res.adressbarheight = undefined;
		if((typeof nbdevice === "object") && (nbdevice.tactileadressbarheight !== undefined)){
			res.adressbarheight = nbdevice.tactileadressbarheight;
		}
		if (window.navigator) {
			var brs = ["webkit","opera","netfront","firefox","iemobile","msie"];
			var ua = window.navigator.userAgent.toLowerCase();
			for ( var k = 0; k < brs.length; k++) {
				if (ua.indexOf(brs[k]) != -1) {
					res.name = brs[k];
					res.cssprefix = cssprefixes[res.name];
				}
			}
		}
		var tmpdiv = document.createElement('div');
		res.canOverflowScroll = tmpdiv.style[res.cssprefix+"overflow-scrolling"] !== undefined;
		var _hOP = ( {}).hasOwnProperty, _hasOwnProperty;
		if (typeof _hOP !== 'undefined' && typeof _hOP.call !== 'undefined') {
			_hasOwnProperty = function(o, p) {
				return _hOP.call(o, p);
			};
		} else {
			_hasOwnProperty = function(o, p) {
				return ((p in o) && typeof o.constructor.prototype[p] === 'undefined');
			};
		}
		function test(prop, props) {
			for ( var i in props) {
				if (ts[props[i]] !== undefined) {
					res.style[prop] = props[i];
					return true;
				}
			}
		}
		function check(prop) {
			var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
			props = [prop, 'Webkit' + uc_prop, 'Moz' + uc_prop, 'O' + uc_prop,'ms' + uc_prop ];
			return !!test(prop, props);
		}
		features[cssborderradius] = function() {
			return check('borderRadius');
		};
		features[cssanimations] = function() {
			return check('animationName');
		};
		features[csstransitions] = function() {
			if (check('transitionProperty')) {
				check('transitionDuration');
				check('transitionTimingFunction');
				check('transitionDelay');
				return true;
			} else {
				return false;
			}
		};
		features[csstransforms] = function() {
			return !!test('transformProperty', [ 'WebkitTransform', 'MozTransform',	'OTransform', 'msTransform' ]);
		};
		features[csstransforms3d] = function() {
			var ret = !!test('perspectiveProperty', [ 'WebkitPerspective','MozPerspective', 'OPerspective', 'msPerspective' ]);
			return ret;
		};
		for ( var f in features) {
			if (_hasOwnProperty(features, f)) {
				res[f.toLowerCase()] = features[f]();
			}
		}
		t = null;
		return res;
	})(),
	screen: {
		getWidth: function () {
			if (typeof netbiscuits !== "undefined" && netbiscuits.screen !== "undefined" && netbiscuits.screen.resizeEvent !== "undefined") {
				return netbiscuits.screen.getWidth();
			} else {
				var w = 320;
				if (tactile.c.D.browser.name === "webkit") {
					w = document.documentElement.clientWidth;
				} else {
					if( typeof( window.innerWidth ) == 'number' ) {
						w = window.innerWidth;
					} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
						w = document.documentElement.clientWidth;
					} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
						w = document.body.clientWidth;
					} else if (window.screen.availWidth) {
						w = window.screen.availWidth;
					}
				}
				return w;
			}
		},
		getHeight: function() {
			if (typeof netbiscuits !== "undefined" && netbiscuits.screen !== "undefined" && netbiscuits.screen.resizeEvent !== "undefined") {
				return netbiscuits.screen.getHeight();
			} else {
				var h = 480;
				if (tactile.c.D.browser.name === "webkit") {
					h = document.documentElement.clientHeight;
				} else {
					if( typeof( window.innerWidth ) == 'number' ) {
						h = window.innerHeight;
					} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
						h = document.documentElement.clientHeight;
					} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
						h = document.body.clientHeight;
					} else if (window.screen.availWidth) {
						h = window.screen.availHeight;
					}
				}
				return h;
			}
		},
		updateOrientation: function() {
			if (tactile.c.D.screen.getHeight() > tactile.c.D.screen.getWidth()) {
				document.body.className = "portrait";
			} else {
				document.body.className = "landscape";
			}
		}
	}
};
setTimeout(
		function() {
			if (window.addEventListener) {
			    var div = document.createElement('div');
			    var handler = function(e) {
			    	tactile.c.D.browser.transitionEndEvent = e.type;
			        this.removeEventListener("webkitTransitionEnd", arguments.callee);
			        this.removeEventListener("OTransitionEnd", arguments.callee);
			        this.removeEventListener("transitionend", arguments.callee);
			        this.removeEventListener("transitionEnd", arguments.callee);
			    };
			    div.setAttribute("style", "position:absolute;top:0px;transition:top 1ms ease;-webkit-transition:top 1ms ease;-moz-transition:top 1ms ease;-o-transition:top 1ms ease");
			    div.addEventListener("webkitTransitionEnd", handler, false);
			    div.addEventListener("OTransitionEnd", handler, false);
			    div.addEventListener("transitionend", handler, false);
			    div.addEventListener("transitionEnd", handler, false);
			    document.documentElement.appendChild(div);
			    setTimeout(function() {
			        div.style.top = "100px";
			        setTimeout(function() {
			            div.parentNode.removeChild(div);
			            div = handler = null;
			        }, 100);
			    }, 0);
			}
		}
		, 10);
tactile.c.E = {
		isHTMLElement: function(elem) {
			if(elem && elem !== null){
				if (typeof HTMLElement === 'object') {
					return (elem instanceof HTMLElement || (elem && elem.nodeType && elem.nodeType == 1));
				} else {
					return (elem && elem.nodeType && elem.nodeType == 1);
				}
			}
			return false;
		},
		getOffsetLeft:function(element) {
			var result = 0;
			if (element.offsetParent) {
				while (element.offsetParent) {
					result += element.offsetLeft;
					element = element.offsetParent;
				}
			} else if (element.x) {
				result += element.x;
			}
			return result;
		},
		getOffsetTop:function(element) {
			var result = 0;
			if (element.offsetParent) {
				while (element.offsetParent) {
					result += element.offsetTop;
					element = element.offsetParent;
				}
			} else if (element.y) {
				result += element.y;
			}
			return result;
		},
		getChildByTagname:function(element,tagname) {
			if (tagname) {
				if (element.getElementsByTagName) {
					var elementsRes = element.getElementsByTagName(tagname);
					if (elementsRes && elementsRes.length > 0) {
						return elementsRes[0];
					}
				}
			}
		},
		getTagName: function(element) {
			var tagName;
			tagName = element.tagName;
			if (!tagName) {
				tagName = element.nodeName;
			}
			if (tagName.toUpperCase) {
				tagName = tagName.toUpperCase();
			}
			return tagName;
		},
		hasClass: function(element, elemClass){
			var currClass,classes;
			currClass = element.className;
			classes = currClass.split(" ");
			if(currClass && currClass.length > 0){
				return classes.contains(elemClass);
			}
			return false;
		},
		addClass: function(element,elemClass) {
			if(element){
				var currClass,classes;
				currClass = element.className;
				classes = currClass.split(" ");
				if(currClass && currClass.length > 0){
					classes = currClass.split(" ");
					if(!classes.contains(elemClass)){
						element.className = currClass+" "+elemClass;
					}
				}else{
					element.className = elemClass;
				}
				return element.className;
			}else{
				return null;
			}
		},
		removeClass: function(element,elemClass) {
			if(element){
			var currClass,classes,newClass;
			currClass = element.className;
			classes = currClass.split(" ");
			newClass = "";
			for(var i=0;i<classes.length;i++){
				if(classes[i] != elemClass){
					if(newClass.length > 0){
						newClass += ' '+classes[i];
					}else{
						newClass += classes[i];
					}
				}
			}
			if(newClass.length > 0){
				element.className = newClass;
			}else{
				element.removeAttribute("class");
			}
			return element.className;
			}else{
				return null;
			}
		},
		getCurrStyle: function(element,cssprop) {
			if (element.currentStyle) {
				return element.currentStyle[cssprop];
			} else if (window.getComputedStyle) {
				var style;
				style = window.getComputedStyle(element, false);
				if (style == null) {
					style = window.getComputedStyle(element, null);
				}
				if (tactile.c.D.browser.name === "webkit" &&  cssprop === "margin-right") {
					return style.getPropertyValue("margin-left");
				}
				return style.getPropertyValue(cssprop);
			} else if (document.defaultView && document.defaultView.getComputedStyle) {
				return document.defaultView.getComputedStyle(element, "")[cssprop];
			} else {
				return element.style[cssprop];
			}
		},
		getStyleValue: function(element,style) {
			var tmp, index, result;
			result = 0;
				tmp = tactile.c.E.getCurrStyle(element,style);
				if (tmp !== undefined) {
					index = tmp.indexOf("px");
					if (index > -1) {
						result += Number(tmp.substring(0, index));
					}
				}
			return result;
		},
		getHorizontalMargin: function(element) {
			var tmp, index, imgMarginH, style;
			imgMarginH = 0;
			tmp = tactile.c.E.getCurrStyle(element,"margin-left");
			if (tmp !== undefined) {
				index = tmp.indexOf("px");
				if (index > -1) {
					imgMarginH += Number(tmp.substring(0, index));
				}
			}
			tmp = tactile.c.E.getCurrStyle(element,"margin-right");
			if (tmp !== undefined) {
				index = tmp.indexOf("px");
				if (index > -1) {
					imgMarginH += Number(tmp.substring(0, index));
				}
			}
			return imgMarginH;
		},
		getVerticalMargin: function(element) {
			var tmp, index, imgMarginV, style;
			imgMarginV = 0;
			tmp = tactile.c.E.getCurrStyle(element,"margin-top");
			if (tmp !== undefined) {
			index = tmp.indexOf("px");
				if (index > -1) {
					imgMarginV += Number(tmp.substring(0, index));
				}
			}
			tmp = tactile.c.E.getCurrStyle(element,"margin-bottom");
			if (tmp !== undefined) {
				index = tmp.indexOf("px");
				if (index > -1) {
					imgMarginV += Number(tmp.substring(0, index));
				}
			}
			return imgMarginV;
		},
		getOffsetMarginLeft: function(element) {
			var result = 0;
			if (element.offsetParent) {
				while (element.offsetParent) {
					result += tactile.c.E.getStyleValue(element,"margin-left")
							+ tactile.c.E.getStyleValue(element,"border-left-width");
					element = element.offsetParent;
				}
			}
			return result;
		},
		setOpacity: function(element,opacity){
			if(element.style.opacity != undefined){
				element.style.opacity =  ( opacity / 100 );
			}else if(element.style.MozOpacity != undefined){
				element.style.MozOpacity = ( opacity / 100 );
			}else if(element.style.KhtmlOpacity != undefined){
				element.style.KhtmlOpacity = ( opacity / 100 );
			}else if(element.style.filter != undefined){
				element.style.filter = 'alpha(opacity=' + opacity + ')';
			}
		},
		getOpacity: function(element){
			if(element.style.opacity != undefined){
				return (Number(element.style.opacity) * 100);
			}else if(element.style.MozOpacity != undefined){
				return (Number(element.style.MozOpacity) * 100);
			}else if(element.style.KhtmlOpacity != undefined){
				return (Number(element.style.KhtmlOpacity) * 100);
			}else if(element.style.filter != undefined){
				var opacityStyle,opacityValue;
				opacityStyle = element.style.filter;
				opacityValue = parseInt( opacityStyle.replace(')','').replace('alpha(opacity=','') ) / 100;
				if (!isNaN(opacityValue) && opacityValue != '') {
				  return opacityValue;
				}
				return '';
			}
		},
		clear: function(element) {
			try {
				element.innerHTML = "";
			} catch (err) {
			}
			if (element.hasChildNodes()) {
				var children = element.childNodes;
				for ( var i = 0; i < children.length; i++) {
						children[i].parentNode.removeChild(children[i]);
						if (children[i]) {
							tactile.c.E.clear(children[i]);
							children[i].parentNode.removeChild(children[i]);
						}
				}
			}
		},
		setInnerHTML: function(element,html, append) {
			if (append && append === true) {
			} else {
				tactile.c.E.clear(element);
			}
			var newNode;
			if (typeof (HTMLElement) === 'object') {
				if (element instanceof HTMLElement) {
					if (tactile.c.E.isHTMLElement(html)) {
						element.appendChild(html);
					} else {
						element.innerHTML = html;
					}
				}
			} else {
				if (element.nodeType && element.nodeType == 1) {
					if ((typeof html) == 'string') {
						newNode = document.createTextNode(html);
						element.appendChild(html);
					} else if ((typeof html) == 'object') {
						if (tactile.c.E.isHTMLElement(html)) {
							element.appendChild(html);
						}
					}
				}
			}
		},
		getZindex: function(elem){
			var tmp,zindex = 0;
			tmp = tactile.c.E.getCurrStyle(elem,"z-index");
			if(Number(tmp)){
				zindex = Number(tmp);
			}
			return zindex;
		},
		swapClass: function (elem, classOne, classTwo) {
			if(elem && classOne && classTwo) {
				var currClass,classes;
				currClass = elem.className;
				if(currClass && currClass.length > 0){
					classes = currClass.split(" ");
					if(classes.contains(classOne)){
						if(!classes.contains(classTwo)){
							tactile.c.E.replaceClass(elem, classOne, classTwo);
						}else{
							tactile.c.E.removeClass(elem, classTwo);
						}
					}else if(classes.contains(classTwo)){
						tactile.c.E.replaceClass(elem,classTwo,classOne);
					}else{
						tactile.c.E.addClass(elem, classOne);
					}
				}else{
					elem.className = classOne;
				}
				return elem.className;
			}
		},
		toggleClass: function (elem, className) {
			if(elem && className) {
				var currClass,classes;
				currClass = elem.className;
				if(currClass && currClass.length > 0){
					classes = currClass.split(" ");
					if(!classes.contains(className)){
						elem.className = currClass+" "+className;
					}else{
						tactile.c.E.removeClass(elem, className);
					}
				}else{
					elem.className = className;
				}
				return elem.className;
			}
		},
		replaceClass: function (elem, oldClass, newClass) {
			if(elem){
				var currClass,classes,oldClassIndex,elemClass;
				currClass = elem.className;
				classes = currClass.split(" ");
				oldClassIndex = classes.indexOf(oldClass);
				if(oldClassIndex != -1){
					classes[oldClassIndex] = newClass;
				}
				elemClass = "";
				for(var i=0;i<classes.length;i++){
					if(elemClass.length > 0){
						elemClass += ' '+classes[i];
					}else{
						elemClass += classes[i];
					}
				}
				if(elemClass.length > 0){
					elem.className = elemClass;
				}else{
					elem.removeAttribute("class");
				}
				return elem.className;
			}
		},
		isChildOf: function(el,parent){
			var children = parent.getElementsByTagName('*');
			if(children.length > 0){
				return  Array.prototype.indexOf.call(children,el) !== -1;
			}
			return false;
		},
		getElementsByClassName: function(el,className){
			if(el && className){
				if(el.getElementsByClassName){
					return el.getElementsByClassName(className);
				}else{
					var allElems;
					var elems = [];
					var i, j,match,singClass;
					var thisElem;
					var classes = [];
					var queryString = "";
					var separator = "";
					var elemClasses;
					var refElem = el;
					if(refElem.querySelectorAll){
						if(className){
							elemClasses = className.split(" ");
							for(i=0;i<elemClasses.length;i++){
								queryString +="." + elemClasses[i];
							}
							elems = refElem.querySelectorAll(queryString);
						}
					}else{
						allElems = refElem.getElementsByTagName('*');
						for (i = 0; i < allElems.length; i++) {
							thisElem = allElems[i];
							if(thisElem.className){
								match = true;
								classes = thisElem.className.split(" ");
								for(j = 0; j < elemClasses.length; j++){
									if(!classes.contains(elemClasses[j])){
										match = false;
										break;
									}
								}
								if(match){
									elems[elems.length] = thisElem;
								}
							}
						}
					}
					return elems;
				}
			}
		},
		translate: function(el,x,y){
			if(el){
				x = tactile.c.U.parsePixel(x);
				y = tactile.c.U.parsePixel(y);
				tactile.c.E.transform(el,"translate",x,y);
			}
		},
		translateX: function(el,x){
		if(el){
			x = tactile.c.U.parsePixel(x);
			tactile.c.E.transform(el,"translateX",x);
		}
		},
		translateY: function(el,y){
			if(el){
				y = tactile.c.U.parsePixel(y);
				tactile.c.E.transform(el,"translateY",y);
			}
		},
		translateZ: function(el,z){
			if(el){
				z = tactile.c.U.parsePixel(z);
				tactile.c.E.transform(el,"translateZ",z);
			}
		},
		translate3d: function(el,x,y,z){
			if(el){
				x = tactile.c.U.parsePixel(x);
				y = tactile.c.U.parsePixel(y);
				z = tactile.c.U.parsePixel(z);
				if(tactile.c.D.browser.csstransforms3d){
					tactile.c.E.transform(el,"translate3d",x,y,z);
				}else{
					tactile.c.E.transform(el,"translate",x,y);
				}
			}
		},
		scale: function(el,factor){
			if(el){
				factor = parseFloat(factor);
				tactile.c.E.transform(el,"scale",factor,factor);
			}
		},
		scaleX: function(el,factor){
			if(el){
				factor = parseFloat(factor);
				tactile.c.E.transform(el,"scaleX",factor);
			}
		},
		scaleY: function(el,factor){
			if(el){
				factor = parseFloat(factor);
				tactile.c.E.transform(el,"scaleY",factor);
			}
		},
		scaleZ: function(el,factor){
			if(el){
				factor = parseFloat(factor);
				tactile.c.E.transform(el,"scaleZ",factor);
			}
		},
		scale3d: function(el,factor){
			if(el){
				factor = parseFloat(factor);
				if(tactile.c.D.browser.csstransforms3d){
					tactile.c.E.transform(el,"scale3d",factor,factor,factor);
				}else{
					tactile.c.E.transform(el,"scale",factor,factor);
				}
			}
		},
		rotate: function(el,angle){
			if(el){
				angle = tactile.c.U.parseDeg(angle);
				tactile.c.E.transform(el,"rotate",angle);
			}
		},
		rotateX: function(el,angle){
		if(el){
			angle = tactile.c.U.parseDeg(angle);
			tactile.c.E.transform(el,"rotateX",angle);
		}
		},
		rotateY: function(el,angle){
			if(el){
				angle = tactile.c.U.parseDeg(angle);
				tactile.c.E.transform(el,"rotateY",angle);
			}
		},
		rotateZ: function(el,angle){
			if(el){
				angle = tactile.c.U.parseDeg(angle);
				tactile.c.E.transform(el,"rotateZ",angle);
			}
		},
		rotate3d: function(el,angle){
			if(el){
				angle = tactile.c.U.parseDeg(angle);
				if(tactile.c.D.browser.csstransforms3d){
					tactile.c.E.transform(el,"scale3d",angle,angle,angle);
				}else{
					tactile.c.E.transform(el,"scale",angle,angle);
				}
			}
		},
		transform: function(el,action,x,y,z){
			var regs = {
					scale:/scale\(.*?\)/,
					scaleX:/scaleX\(.*?\)/,
					scaleY:/scaleY\(.*?\)/,
					scaleZ:/scaleZ\(.*?\)/,
					scale3d:/scale3d\(.*?\)/,
					translate:/translate\(.*?\)/,
					translateX:/translateX\(.*?\)/,
					translateY:/translateY\(.*?\)/,
					translateZ:/translateZ\(.*?\)/,
					translate3d:/translate3d\(.*?\)/,
					rotateX:/rotateX\(.*?\)/,
					rotateY:/rotateY\(.*?\)/,
					rotateZ:/rotateZ\(.*?\)/,
					rotate:/rotate\(.*?\)/,
					rotate3d:/rotate3d\(.*?\)/
			};
			var transform = el.style[tactile.c.D.browser.style.transformProperty];
			var tr = null;
			if(arguments.length === 3){
				tr = action+"("+x+")";
			}else if(arguments.length === 4){
				tr = action+"("+x+","+y+")";
			}else if(arguments.length === 5){
				tr = action+"("+x+","+y+","+z+")";
			}
			if(tr !== null ){
				if(transform.length > 0){
					if(transform.indexOf(action) !== -1){
						el.style[tactile.c.D.browser.style.transformProperty] = transform.replace(regs[action],tr);
					}else{
						el.style[tactile.c.D.browser.style.transformProperty] = transform+" "+tr;
					}
				}else{
					el.style[tactile.c.D.browser.style.transformProperty] = tr;
				}
			}
		}
}
tactile.c.U = {
	setBodyStyle: function(style, value) {
		if (tactile.c.D.browser.name !== "msie") {
			document.body.style[style] = value;
		} else {
		}
	},
	applyTransformFlickerFix: function(elem) {
		var pf = tactile.c.D.os.platform, bn = tactile.c.D.browser.name, vs = tactile.c.D.os.version;
		if (((pf === "android") && (bn === "webkit")) || ((pf === "ios") && (vs > 3))){
			elem.style.webkitBackfaceVisibility = "hidden";
		}
	},
	removeTransformFlickerFix: function(elem) {
		var pf = tactile.c.D.os.platform, bn = tactile.c.D.browser.name, vs = tactile.c.D.os.version;
		if (((pf === "android") && (bn === "webkit")) || ((pf === "ios") && (vs > 3))){
			elem.style.webkitBackfaceVisibility = "";
			try{
				elem.style.removeProperty("-webkit-backface-visibility");
			}catch(e){
			}
		}
	},
	updateRenderTreeFix: function(elem) {
		var getwidth = function(v) {
			var b = v.offsetWidth = v.offsetHeight;
			if(v.style && v.style.width){
				return Number(v.style.width.replace('px',''));
			}
			return v.offsetWidth;
		};
		if(elem){
			var x = elem.offsetWidth, x = elem.offsetHeight, x = elem.offsetLeft, x = elem.offsetTop;
			elem.style.width = (getwidth(elem) + 1) +"px";
			elem.style.width = (getwidth(elem) - 1) +"px";
		}
		var f = document.getFirstBodyChild();
		f.style.width = (getwidth(f) + 1) +"px";
		f.style.width = (getwidth(f) - 1) +"px";
	},
	iOS3: function() {
		var pf = tactile.c.D.os.platform, vs = tactile.c.D.os.version;
		return (pf === "ios" && vs === "3");
	},
	getUID: function(){
		return ((new Date()).getTime() + "" + Math.floor(Math.random()*1000000)).substr(0, 18);
	},
	mixin: function (dst, src){
	    var tobj = {}
	    for(var x in src){
	       if((typeof tobj[x] == "undefined") || (tobj[x] != src[x])){
	          dst[x] = src[x];
	       }
	    }
	 },
     extend: function(Child, Parent) {
    	var F = function() { }, obj = {};
 		this.mixin(obj, Child.prototype);
 		F.prototype = Parent.prototype;
 		Child.prototype = new F();
 		Child.prototype.constructor = Child;
 		Child.superclass = Parent.prototype;
 		Child.extend(obj);
     },
    fixLinks : function(){
    	for(var i = 0; i < document.links.length; i++) {
    		if(!document.links[i].url){
    			document.links[i].url = document.links[i].href;
    			document.links[i].href = "javascript:function(){return false;}";
    			document.links[i].addEventListener("touchstart",function(e){
    				tactile.page.isClick = true;
	    			var tmp = this.href;
	    			this.href = "javascript:function(){return false;}";
	    			window.setTimeout(function(){
	    				if(tactile.page.isClick){
	    					this.href = this.url;
	    				}
	    			}.bind(this),300);
    			},false);
    			document.links[i].addEventListener("click",function(e){
    				if(tactile.page.isClick){
    					window.location = this.url;
    					tactile.page.isClick = true;
    				}
    			},false);
    			document.links[i].addEventListener("touchend",function(e){
					if(tactile.page.isClick){
						window.location = this.url;
						tactile.page.isClicke = true;
					}
					this.href = "javascript:function(){return false;}";
    			},false);
    		}
    	}
    },
    attrValidator: {
        scriptString : '',
        leftString: '',
        rules: {
            getComponent: function(){
                var componentString = tactile.c.U.attrValidator.scriptString.substring(0, tactile.c.U.attrValidator.scriptString.indexOf('.')),
                    comp = null;
                var firstCommand = componentString != '' ? componentString : tactile.c.U.attrValidator.scriptString;
                if(firstCommand.indexOf('#') > -1) firstCommand = firstCommand.substring(1, firstCommand.length);
                comp = (!tactile.page.getComponent(firstCommand) ? window[firstCommand] : tactile.page.getComponent(firstCommand));
                if(!comp){
                    throw "Component is not exists";
                }
                else{
                    tactile.c.U.attrValidator.leftString = tactile.c.U.attrValidator.scriptString.substring(componentString.length + 1, tactile.c.U.attrValidator.scriptString.length);
                }
                return this.getComponentPath(comp);
            },
            getComponentPath: function(comp){
                var regex = /^([a-zA-Z0-9.]+)\.[a-zA-Z0-9.\(]+/,
                    propertyStringArr = tactile.c.U.attrValidator.leftString.match(regex);
                if(propertyStringArr){
                    var propertyString = propertyStringArr[1];
                    propertyStringArr = propertyString.split('.');
                    for(var i = 0, j = propertyStringArr.length; i< j; i++){
                        if(comp[propertyStringArr[i]]){
                            comp = comp[propertyStringArr[i]];
                        } else {
                            throw "Component is not exists";
                        }
                    }
                    tactile.c.U.attrValidator.leftString = tactile.c.U.attrValidator.leftString.replace(propertyString+'.', '');
                }
                return this.getMethod(comp);
            },
            getMethod: function(comp){
                var regex = /^(?:([a-zA-Z0-9.]+\(.*\))[.].*|([a-zA-Z0-9.]+\(.*\)))$/,
                    func = tactile.c.U.attrValidator.leftString.match(regex),
                    runComponent = null;
                if(func){
                    func = (func[1] !== undefined ? func[1] : func[2]);
                    var funcName = func.substring(0, func.indexOf('(')),
                        funcArgs = func.match(/\((.*)\)$/);
                    if(comp[funcName]){
                        var argument = null;
                        if(funcArgs){
                            try{
                                argument = eval('['+funcArgs[1]+']');
                            } catch(e) {
                                throw 'Invalid arguments for function'
                            }
                        }
                        if(tactile.c.U.attrValidator.leftString.replace(func, '') != ''){
                            comp = comp[funcName].apply(null, argument);
                            tactile.c.U.attrValidator.leftString = tactile.c.U.attrValidator.leftString.replace(func+'.', '');
                            return this.getComponentPath(comp);
                        } else {
                            runComponent = {
                                component: comp,
                                endFunction: funcName,
                                functionArguments: argument,
								scriptString : tactile.c.U.attrValidator.scriptString
                            }
                        }
                    }
                } else {
                    runComponent = {
                        component: comp,
                        endFunction: 'show',
                        functionArguments: [],
						scriptString : tactile.c.U.attrValidator.scriptString
                    };
                }
                return runComponent;
            }
        },
        validate: function(str, autorun){
            this.scriptString = str;
            try{
                var comp = this.rules.getComponent();
                if(autorun){
                    comp.component[comp.endFunction].apply(comp.component, comp.functionArguments);
                }
                return comp;
            } catch(e){
                console.error(e);
            }
        }
    },
    getURLParam: function(pName){
    	var result = "";
    	var strHref = window.location.href;
    	if (strHref.indexOf("?") > -1) {
    		var strQueryString = strHref.substr(strHref.indexOf("?") + 1);
    		var aQueryString = strQueryString.split("&");
    		for ( var iParam = 0; iParam < aQueryString.length; iParam++) {
    			if (aQueryString[iParam].indexOf(pName + "=") === 0) {
    				var aParam = aQueryString[iParam].split("=");
    				result = aParam[1];
    				var sqPos = result.indexOf("#");
    				if (sqPos > -1) {
    					result = result.substring(0, sqPos);
    				}
    				break;
    			}
    		}
    	}
    	return unescape(result);
    },
    parsePixel: function(param){
    	param = param.toString();
    	return parseFloat(param) + (param.match(/px/) || 'px');
    },
    parseDeg: function(param){
    	param = param.toString();
    	return parseFloat(param) + (param.match(/deg/) || 'deg');
    }
};
tactile.c.A = {
		rootPath:null,
		getXHRObject: function() {
			var xhr;
			if (window.XMLHttpRequest) {
				xhr = new XMLHttpRequest();
			} else if (window.ActiveXObject) {
				try {
					xhr = new ActiveXObject("Microsoft.XMLHTTP");
				} catch (e1) {
					try {
						xhr = new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e2) {
						try {
							xhr = new ActiveXObject("Msxml2.XMLHTTP.4.0");
						} catch (e3) {
							try {
								xhr = new ActiveXObject("Msxml2.XMLHTTP.5.0");
							} catch (e4) {
								xhr = undefined;
							}
						}
					}
				}
			} else {
				xhr = undefined;
			}
			if(xhr != undefined){
				xhr.getResponseDom = function(){
					var response,responseDom;
					response = xhr.responseText;
					responseDom = null;
					if(response && response != null){
						responseDom = tactile.c.O.stringToDom(response);
					}else{
						response = xhr.responseXML;
						if(response && response != null){
							responseDom = tactile.c.O.xmlToDom(response);
							if(responseDom.nodeType != 1){
								responseDom = tactile.c.O.getElementNode(responseDom);
							}
						}
					}
					return responseDom;
				};
			}
			return xhr;
		},
		getRootPath:function() {
			var metas, i, meta, j, name, value, appPath, rifPath;
			if (tactile.c.A.rootPath === null) {
				var root = null;
				if (typeof nbdevice == "object") {
					if (name !== null && value !== null) {
						if (nbdevice.internalapppath) {
							appPath = nbdevice.internalapppath;
						}
						if (nbdevice.rifpath) {
							rifPath = nbdevice.rifpath;
						}
					}
				}
				if (rifPath === null || rifPath.length === 0) {
					rifPath = "nbrif";
				}
				if (!(rifPath.indexOf("http") === 0)) {
					rifPath = appPath + rifPath;
				}
				tactile.c.A.rootPath = rifPath;
			}
			return tactile.c.A.rootPath;
		},
		initScript: function(element){
			var refNode = (typeof element == "string") ? document.getElementById(element) : element;
			if(refNode){
			var scripts = refNode.getElementsByTagName("script");
				for(var i=0; i<scripts.length; i++){
					eval(scripts[i].innerHTML);
				}
			}else{
				throw new tactile.c.Error("No Reference node found !");
			}
		},
		getSessionID: function() {
			if(tactile.c.D.browser.canCookie){
				return ";jsessionid="+tactile.c.C.getCookie("JSESSIONID");
			}else{
				var url,paramStart,jsessionStart,jsession;
				url =  window.location.href;
				paramStart = url.indexOf("?");
				jsessionStart = url.indexOf(";jsessionid=");
				if(paramStart > jsessionStart){
					jsession = url.substring(jsessionStart,paramStart);
				}else{
					jsession = url.substring(jsessionStart);
				}
				return jsession;
			}
		}
	};
tactile.c.O = {
	getElementNode: function(xml) {
		var element;
		var children = xml.childNodes;
		if (xml.nodeType == 9 || xml.nodeType == 10 || xml.nodeType == 11) {
			for ( var i = 0; i < children.length; i++) {
				if (children[i].nodeType == 1) {
					element = children[i];
					break;
				}
			}
		} else {
			element = children[0];
		}
		return element;
	},
	stringToDom: function(string_) {
		var domObject, tmpContainer;
		tmpContainer = document.createElement("div");
		tmpContainer.innerHTML = string_;
		if (tmpContainer.childNodes.length > 0) {
			domObject = tmpContainer.firstChild;
		} else {
			var regex = new RegExp("(<\/?[^>]+>)", "g");
			if (!(regex.test(string_))) {
				var str = document.createTextNode(string_);
				tmpContainer.appendChild(str);
			} else {
				tmpContainer = this.stringToDomParts(string_);
			}
			if (tmpContainer.childNodes.length > 0) {
				domObject = tmpContainer.firstChild;
			}
		}
		return domObject;
	},
	stringToDomParts: function(string_, refNode){
		if(!refNode) var refNode = document.createElement("div");
		var regex = new RegExp("(<\/?[^>]+>)", "g"),
		buffer = string_.split(regex),
		content = false,
		tagName = false,
		tagAttr = false;
		for ( var i = 0; i < buffer.length; i++) {
			if (tagName) {
				if (/<\/[^>]+>/.test(buffer[i])
						&& buffer[i].substring(2, buffer[i].length - 1) == tagName) {
					var node = document.createElement(tagName);
					if(tagAttr && tagAttr.length > 0){
						for(var m = 0, v = tagAttr.length; m < v; m++){
							var tempAttr = tagAttr[m].replace(/['"]/g,'');
							node.setAttribute(tempAttr.split('=')[0].trim(), tempAttr.split('=')[1].trim());
						}
					}
					arguments.callee(content, node);
					refNode.appendChild(node);
					tagName = false;
					content = false;
				} else
					content += buffer[i];
			} else if (!(regex.test(buffer[i])) && buffer[i] != "")
				refNode.appendChild(document.createTextNode(buffer[i]));
			else if (/<[^>]+>/.test(buffer[i])) {
				content = "";
				tagName = buffer[i].substring(1, buffer[i].length - 1);
				if(tagName.split(' ').length > 1) {
					tagAttr = tagName.match(/(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?/g);
					tagName = tagName.split(' ')[0];
				}
			}
		}
		return refNode;
	},
	xmlToDom: function(xmlnode) {
		var root, current, attrs, currAttr, tmp, children;
		if (xmlnode.nodeType == 9 || xmlnode.nodeType == 10
				|| xmlnode.nodeType == 11) {
			children = xmlnode.childNodes;
			for ( var i = 0; i < children.length; i++) {
				if (children[i].nodeType == 1) {
					root = children[i];
					break;
				}
			}
		} else {
			root = xmlnode;
		}
		if (root.nodeType == 1) {
			current = document.createElement(root.nodeName);
			attrs = root.attributes;
			if (attrs && attrs.length > 0) {
				for ( var i = 0; i < attrs.length; i++) {
					currAttr = document.createAttribute(attrs[i].nodeName);
					currAttr.nodeValue = attrs[i].nodeValue;
					current.setAttributeNode(currAttr);
				}
			}
			if (root.hasChildNodes()) {
				children = root.childNodes;
				for ( var j = 0; j < children.length; j++) {
					current.appendChild(tactile.c.O.xmlToDom(children[j]));
				}
				return current;
			} else {
				return current;
			}
		} else {
			if (root.nodeType == 3 || root.nodeType == 4) {
				current = document.createTextNode(root.nodeValue);
				return current;
			} else {
				current = document.createTextNode("");
				return current;
			}
		}
	}
};
tactile.c.V = {
	listeners : {},
	addResizeListener : function(fn, scope) {
		if (typeof netbiscuits !== "undefined" && netbiscuits.screen !== "undefined" && netbiscuits.screen.resizeEvent !== "undefined") {
			var resizefn = function(w, h) {
				var resizeevent = {
					screenWidth : w,
					screenHeight : h
				};
				fn.call(scope || fn, resizeevent);
			};
			netbiscuits.screen.resizeEvent.add(resizefn);
		} else {
			var resizefn = function(e) {
				fn.call(scope || fn, e);
			};
			tactile.c.V.resizeEvent.add(resizefn);
		}
	},
	addManagedListener : function(el, ev, fn, scope) {
		var managedfn;
		fn.tactileUID = tactile.c.U.getUID();
		if(ev.toLowerCase().indexOf("resize") !== -1 || ev.toLowerCase().indexOf("orientationchange") !== -1){
			tactile.c.V.addResizeListener(fn);
			return true;
		} else if ("domcontentloaded".indexOf(ev.toLowerCase()) !== -1) {
			tactile.c.V.domReady.add(fn);
			return true;
		} else {
			if ("click|mousedown|mouseup|mousemove".indexOf(ev.toLowerCase()) !== -1) {
				managedfn = function(e) {
					fn.call(e, tactile.c.V.normalizeMouseEvent(e));
				};
			} else {
				managedfn = function(e) {
					scope = scope || window;
					fn.call(scope, tactile.c.V.normalizeEvent(e));
				};
			}
			managedfn.tactileUID = fn.tactileUID;
			return tactile.c.V.addListener(el, ev, managedfn);
		}
	},
	addListener : function(el, ev, fn) {
		if (el.addEventListener) {
			tactile.c.V.addListner = function(el, ev, fn) {
				var uid = fn.tactileUID ? fn.tactileUID : tactile.c.U.getUID();
				tactile.c.V.listeners[uid] = {element:el, event:ev};
				window["anFunc_"+uid] = function(evt){fn(evt);}
				tactile.c.V.listeners[uid].handler = window["anFunc_"+uid];
				el.addEventListener(ev, tactile.c.V.listeners[uid].handler, false);
				return uid;
			};
		} else {
			throw new tactile.c.Error("browser do not support addEventListener");
		}
		return tactile.c.V.addListner(el, ev, fn);
	},
	removeManagedListener : function(el, ev, fn) {
		var managedfn;
		if ("click|mousedown|mouseup|mousemove".indexOf(ev.toLowerCase()) !== -1) {
			managedfn = function(e) {
				fn.call(e, tactile.c.V.normalizeMouseEvent(e));
			};
		} else {
			managedfn = function(e) {
				fn.call(e, tactile.c.V.normalizeEvent(e));
			};
		}
		var uid,h;
		if(typeof el == "string"){
			uid = el;
		}else{
			uid = fn.tactileUID;
		}
		if(uid in tactile.c.V.listeners){
			h = tactile.c.V.listeners[uid].handler;
			el = tactile.c.V.listeners[uid].element;
			tactile.c.V.removeListener(el, ev, h);
			delete tactile.c.V.listeners[uid];
		}
	},
	removeListener : function(el, ev, fn) {
		if (el.addEventListener) {
			tactile.c.V.removeListener = function(el, ev, fn) {
				el.removeEventListener(ev, fn, false);
			};
		} else {
			throw new tactile.c.Error("browser do not support addEventListener");
		}
		tactile.c.V.removeListener(el, ev, fn);
	},
	normalizeEvent : function(e) {
		e = e || window.event;
		var ret = {
			browserEvent: e,
			target: e.target || e.srcElement,
			type: e.type
		};
		return ret;
	},
	normalizeMouseEvent : function(e) {
		e = e || window.event;
		var ret = {
			browserEvent: e,
			target: e.target || e.srcElement,
			type: e.type,
			screenX: e.screenX,
			screenY: e.screenY,
			pageX: e.pageX || e.clientX,
			pageY: e.pageY || e.clientY,
			clientX: e.clientX,
			clientY: e.clientY,
			offsetX: e.offsetX || e.clientX,
			offsetY: e.offsetY || e.clientY,
			timeStamp: e.timeStamp || new Date().getTime()
		};
		return ret;
	},
	domReady : {
		bInit: false,
		events : {},
		domReadyID : 1,
		bDone : false,
		add : function(fn) {
			var self = tactile.c.V.domReady;
			if (!self.bInit) {
				self.init();
			}
			if (!fn.$$domReadyID) {
				fn.$$domReadyID = self.domReadyID++;
				if (self.bDone) {
					fn();
				}
				self.events[fn.$$domReadyID] = fn;
			}
		},
		run : function() {
			var self = tactile.c.V.domReady;
			if (self.bDone) {
				return;
			}
			self.bDone = true;
			for ( var i in self.events) {
				self.events[i]();
			}
		},
		init : function() {
			if (document.addEventListener) {
				document.addEventListener("DOMContentLoaded",
					function () {
						document.removeEventListener("DOMContentLoaded", arguments.callee, false );
						tactile.c.V.domReady.run();
					}, false);
			} else {
				throw new tactile.c.Error("browser do not support addEventListener");
			}
			tactile.c.V.domReady.bInit = true;
		}
	},
	resizeEvent: {
		events: {},
		eventID: 1,
		bInit: false,
		width: 0,
		height: 0,
		add: function(fn) {
			var self = tactile.c.V.resizeEvent;
			if (!self.bInit) {
				self.init();
			}
			if (!fn.$$eventID) {
				fn.$$eventID = self.eventID++;
				self.events[fn.$$eventID] = fn;
			}
		},
		run: function() {
			var self = tactile.c.V.resizeEvent;
			var resizeevent = {
				screenWidth : self.width,
				screenHeight : self.height
			};
			for (var i in self.events) {
				self.events[i](resizeevent);
			}
		},
		init: function() {
			var self = tactile.c.V.resizeEvent;
			var ev = "resize";
			if (tactile.c.D.os.platform === 'ios') {
				ev = "orientationchange";
			} else {
	            if ("onresize" in window) {
	                ev = "resize";
	            } else if ("onorientationchange" in window) {
	                ev = "orientationchange";
	            }
			}
            self.width = tactile.c.D.screen.getWidth();
            self.height = tactile.c.D.screen.getHeight();
            self.bInit = true;
            tactile.c.V.addListener(window, ev, self.fire);
		},
		fire: function() {
			setTimeout("tactile.c.V.resizeEvent.fireImpl(" + 10 + ")", 100);
		},
		fireImpl: function(count) {
			var self = tactile.c.V.resizeEvent;
			var newWidth, newHeight;
			newWidth = tactile.c.D.screen.getWidth();
			newHeight = tactile.c.D.screen.getHeight();
			if (self.width !== newWidth) {
				self.width = newWidth;
				self.height = newHeight;
				self.run();
			} else {
				if (count > 0) {
					setTimeout("tactile.c.V.resizeEvent.fireImpl(" + (count - 1) + ")", 100);
				}
			}
		}
	}
};
var JSON;
if (!JSON) {
    JSON = {};
}
(function () {
    "use strict";
    function f(n) {
        return n < 10 ? '0' + n : n;
    }
    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function (key) {
            return isFinite(this.valueOf()) ?
                this.getUTCFullYear()     + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate())      + 'T' +
                f(this.getUTCHours())     + ':' +
                f(this.getUTCMinutes())   + ':' +
                f(this.getUTCSeconds())   + 'Z' : null;
        };
    }
    if (typeof String.prototype.toJSON !== 'function') {
	    String.prototype.toJSON      =
	    Number.prototype.toJSON  =
	    Boolean.prototype.toJSON = function (key) {
	        return this.valueOf();
	    };
    }
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;
    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c :
                '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }
    function str(key, holder) {
        var i,
            k,
            v,
            length,
            mind = gap,
            partial,
            value = holder[key];
        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
        case 'string':
            return quote(value);
        case 'number':
            return isFinite(value) ? String(value) : 'null';
        case 'boolean':
        case 'null':
            return String(value);
        case 'object':
            if (!value) {
                return 'null';
            }
            gap += indent;
            partial = [];
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }
                v = partial.length === 0 ? '[]' : gap ?
                    '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
                    '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }
            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === 'string') {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }
            v = partial.length === 0 ? '{}' : gap ?
                '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
                '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }
    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw tactile.c.Error('JSON.stringify');
            }
            return str('', {'': value});
        };
    }
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
            var j;
            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }
            throw tactile.c.Error('JSON.parse');
        };
    }
}());
if (tactile.effects == null || typeof (tactile.effects) != "object") {
	tactile.effects = {};
}
if (tactile.events == null || typeof (tactile.events) != "object") {
	tactile.events = {};
}
if (tactile.foundation == null || typeof (tactile.foundation) != "object") {
	tactile.foundation = {};
}
if (tactile.errors == null || typeof (tactile.errors) != "object") {
	tactile.errors = {};
}
tactile.errors.Base = function() {
	this.init();
};
tactile.errors.Base.prototype = {
	init: function(name, message, number){
		this.name = name;
	    this.message = message;
	    this.number = number;
	},
	toString: function(){
		return this.name+" - "+this.number+": "+this.message;
	},
	printStackTrace: function(){
		if(tactile.logger.level === tactile.logger.level.error){
			var callstack = [];
			var e = new Error(this.message);
			if (e.stack) {
			     var lines = e.stack.split('\n');
			     for (var i=0, len=lines.length; i<len; i++) {
			         callstack.push(lines[i]);
			     }
			     callstack.shift();
			}else{
				var currentFunction = arguments.callee.caller;
			    while (currentFunction) {
			      var fname = currentFunction.name ? currentFunction.name : (/\W*function\s+([\w\$]+)\(/.exec(currentFunction.toString()))[1];
			      if(fname){
			    	  callstack.push(fname);
			      }
			      currentFunction = currentFunction.caller;
			    }
			}
			Logger.prototype.error(callstackr.join('\n\n'));
		}
	}
};
tactile.errors.TypeError = function(func,expected,given){
	this.init(func, expected, given);
};
tactile.errors.TypeError.prototype = {
	init: function(func, expected, given){
		tactile.errors.TypeError.superclass.init.call(this, "TypeError", ("The function "+func+" expects an argument of type "+expected+", but was "+given), 1004);
		this.printStackTrace();
	}
};
tactile.c.U.extend(tactile.errors.TypeError, tactile.errors.Base);
tactile.errors.ArgumentUndefined = function(func,arg){
	this.init(func, arg);
};
tactile.errors.ArgumentUndefined.prototype = {
	init: function(func, arg){
		tactile.errors.ArgumentUndefined.superclass.init.call(this, "ArgumentUndefined", "Argument "+arg+" is required for the function "+func+", but was undefined", 1011);
		this.printStackTrace();
	}
}
tactile.c.U.extend(tactile.errors.ArgumentUndefined, tactile.errors.Base);
tactile.errors.InvalidArgument = function(func,arg){
	this.init(func,arg);
};
tactile.errors.InvalidArgument.prototype = {
	init: function(func, arg){
		tactile.errors.InvalidArgument.superclass.init.call(this, "InvalidArgument", "Wrong value of " + arg + " for the function " + func, 1009);
		this.printStackTrace();
	}
};
tactile.c.U.extend(tactile.errors.InvalidArgument, tactile.errors.Base);
tactile.logger = (function(){
    var AJAX = {
        request: function(){
            var Request = false;
            if (window.XMLHttpRequest){
                Request = new XMLHttpRequest();
            } else if (window.ActiveXObject) {
                Request = new ActiveXObject("Microsoft.XMLHTTP");
                if (!Request)
                {
                    HRequest = new ActiveXObject("Msxml2.XMLHTTP");
                }
            }
            return Request;
        },
        sendRequest: function(r_method, r_path, r_args, r_handler){
            var Request = this.request();
            if (!Request) return;
            Request.onreadystatechange = function(){
                if (Request.readyState == 4) {
                    r_handler(Request);
                }
            };
            if (r_method.toLowerCase() == "get" && r_args.length > 0)
            r_path += "?" + r_args;
            Request.open(r_method, r_path, true);
            if (r_method.toLowerCase() == "post"){
                Request.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=utf-8");
                Request.send(r_args);
            } else {
                Request.send(null);
            }
        }
    };
    var Logger = function() {};
    Logger.extend({
        setConfig: function(config){
        	config.overlayScriptUrl = config.overlayScriptUrl || 'nbinternal/LoggerOverlay.js';
            this.message.isAjax = config.isAjax || false;
            this.message.isOverlay = config.isOverlay || false;
            this.level = config.level || 0;
            this.message.height = config.height || 300;
            this.setLevel(this.level);
            this.filter = config.filter || ['all'];
            if(this.message.isAjax){
                if(config.ajaxUrl)
                    this.message.ajaxUrl = config.ajaxUrl || "";
                else
                    this.message.isAjax = false;
            }
            if(this.message.isOverlay && config.overlayScriptUrl){
                if(!tactile.loggerOverlay){
                    var script = document.createElement("script");
                    script.onload = function(){
                        try{
                            this.message.overlayLoaded = tactile.loggerOverlay.createOverlay();
                        } catch(e) {
                            document.addEventListener("DOMContentLoaded", (function(){
                                this.message.overlayLoaded = tactile.loggerOverlay.createOverlay();
                            }).bind(this), false);
                        }
                    }.bind(this);
                    script.type = 'text/javascript';
                    script.src = config.overlayScriptUrl || "";
                    document.getElementsByTagName("head")[0].appendChild(script);
                } else {
                    this.message.overlayLoaded = tactile.loggerOverlay.createOverlay();
                }
            }
            if(this.message.isOverlay || this.message.isAjax) this.interval((config.timeout || 400));
        },
        interval: function(timeout){
            setInterval((function(){
               if(this.message.isAjax) this.message.sendAjax();
               if(this.message.isOverlay) this.message.printToOverlay();
            }).bind(this), timeout);
        },
        message: {
            ajaxUrl: "",
            updateTime: 0,
            lastUpdateTime: 0,
            tplObj: {
                items: [{
                    element: "ul",
                    items:[{
                        element: "li",
                        key:'level'
                    },{
                        element: "li",
                        key:'message'
                    },{
                        element: "li",
                        key:'date'
                    }]
                }]
            },
            template: function(elements, dataObj){
                var html = [], element, items, i;
                for(i=0; i < elements.length; i++){
                    element = document.createElement(elements[i].element);
                    if(elements[i].items && elements[i].items.length > 0){
                        items = arguments.callee(elements[i].items, dataObj);
                        for(var m = 0; m < items.length; m++){
                            element.appendChild(items[m]);
                        }
                    } else if(elements[i].key){
                        if(dataObj[elements[i].key]){
                            element.appendChild(document.createTextNode(dataObj[elements[i].key]));
                        }
                        element.setAttribute('class', (elements[i].key == 'level' ? elements[i].key +' '+dataObj[elements[i].key] : elements[i].key));
                    }
                    html.push(element);
                }
                return html;
            },
            createHtml: function(){
                var container = document.createElement('div');
                    container.setAttribute('class', "log-container");
                    for(var i = 0; i < this.logCollection.length; i++){
                        var el = this.template(this.tplObj.items, this.logCollection[i]);
                        for(var j = 0; j < el.length; j++)
                            container.appendChild(el[j]);
                    }
                    return container;
            },
            logCollection: [],
            clearResults: function(){
            	if(this.ajaxPrinted && this.overlayPrinted){
    	        	this.lastUpdateTime = this.updateTime;
    	            this.logCollection = [];
            	}
            },
            ajaxPrinted: true,
            overlayPrinted: true,
            sendAjax: function(){
            	if(this.lastUpdateTime != this.updateTime){
                    var container = this.createHtml();
                    AJAX.sendRequest('post', this.ajaxUrl ,(new XMLSerializer().serializeToString(container)), (function(){
                    	this.ajaxPrinted = true;
                    	this.clearResults();
                    }).bind(this));
                }
            },
            printToOverlay: function(){
                if(!this.overlayLoaded) return false;
                if(this.lastUpdateTime != this.updateTime){
                    var container = this.createHtml();
                    tactile.loggerOverlay.print(container);
                    this.overlayPrinted = true;
                    this.clearResults();
                }
            },
            createReport: function(level, message){
                var dateObj = {
                    level: level,
                    message: message || "",
                    date:(new Date()).toGMTString()
                };
                this.logCollection.splice(0, 0, dateObj);
                this.updateTime = (new Date()).getTime();
                this.ajaxPrinted = !this.isAjax;
                this.overlayPrinted = !this.isOverlay;
            }
        },
        filter: ['all'],
        checkFilters: function(message, filter){
            if(!filter || typeof filter != 'string') filter = 'all';
            if(this.filter.contains(filter) || this.filter.contains('all')) return true;
            return false;
        },
        error: function(message, filter) {
            if(!this.checkFilters.apply(this, arguments)) return false;
            if (this.level >= 1) {
                this.message.createReport.call(this.message, 'error', message);
                this.log("error", message);
            }
        },
        warn: function(message, filter) {
            if(!this.checkFilters.apply(this, arguments)) return false;
            if (this.level >= 2) {
                this.message.createReport.call(this.message, 'warn', message);
                this.log("warn", message);
            }
        },
        info: function(message, filter) {
            if(!this.checkFilters.apply(this, arguments)) return false;
            if (this.level >= 3) {
                this.message.createReport.call(this.message, 'info', message);
                this.log("info", message);
            }
        },
        log: function(level, message) {
            if (typeof console === "object" && console.log !== undefined) {
                console.log(level + " - " + message);
            }
        },
        setLevel: function(level) {
            this.level = level;
        }
    });
    return new Logger();
})();
tactile.logger.level = {
    info: 3,
    warn: 2,
    error: 1,
    disabled: 0
};
tactile.Device = {
	type:tactile.c.D.type,
	isSmartphone : (tactile.c.D.type == "Mobile Phone"),
	isTablet : (tactile.c.D.type == "Tablet"),
	isEReader : (tactile.c.D.type == "eReader"),
	isDesktopBrowser : (tactile.c.D.type == "Desktop-Browser"),
	displayCategory: tactile.c.D.displayCategory,
	platform: tactile.c.D.os.platform,
	version: tactile.c.D.os.version,
	isTouch: tactile.c.D.os.isTouch,
	defaults: {
		effectduration: tactile.c.D.os.platform === "android" && tactile.c.D.os.version <= 2 ? 1000 : 800
	},
	browser: {
		name: tactile.c.D.browser.name,
		canCookie: tactile.c.D.browser.canCookie,
		cssanimations: tactile.c.D.browser.cssanimations,
		csstransitions: tactile.c.D.browser.csstransitions,
		csstransforms: tactile.c.D.browser.csstransforms,
		csstransforms3d: tactile.c.D.browser.csstransforms3d,
		adressbarheight: tactile.c.D.browser.adressbarheight,
		borderradius: tactile.c.D.browser.borderradius,
		transitionEndEventType: function() {return tactile.c.D.browser.transitionEndEvent;},
		transitionEndEventSupported: function() {return tactile.c.D.browser.transitionEndEvent !== "";},
		style: {
			transitionProperty: tactile.c.D.browser.style.transitionProperty,
			transitionDuration: tactile.c.D.browser.style.transitionDuration,
			transitionTimingFunction: tactile.c.D.browser.style.transitionTimingFunction,
			transitionDelay: tactile.c.D.browser.style.transitionDelay,
			transformProperty: tactile.c.D.browser.style.transformProperty,
			perspectiveProperty: tactile.c.D.browser.style.perspectiveProperty,
			borderRadius: tactile.c.D.browser.style.borderRadius
		},
		prefixed: function(prop) {
			return tactile.c.D.browser.cssprefix + prop;
		},
		canOverflowScroll: tactile.c.D.browser.canOverflowScroll
	},
	screen: {
		getWidth: function() { return tactile.c.D.screen.getWidth(); },
		getHeight: function() { return tactile.c.D.screen.getHeight(); },
		isLandscape: function() { return tactile.c.D.screen.getWidth() > tactile.c.D.screen.getHeight();},
		isPortrait: function() { return tactile.c.D.screen.getHeight() > tactile.c.D.screen.getWidth();}
	}
};
tactile.events.EventsFactory = (function(){
    return {
        eventNames: 'tap|doubletap|swipe|scroll|scrollstart|scrollend|touchstart|touchmove|touchend|rotate|pinch|spread|press',
        getEvent: function(eventName, element, callback, stopPropagation){
            if(!element && !callback) throw "Error";
            if(this.isEventExists(eventName)){
                try{
                    stopPropagation = stopPropagation || false;
                    return new tactile.events[eventName.capitalize()](element, callback, stopPropagation);
                } catch (error) {
                    throw error;
                };
            }
        },
        isEventExists: function(eventName){
            return (~this.eventNames.indexOf(eventName) ? true : false);
        }
    };
})();
tactile.events.TouchEventManager = (function(){
    var TouchManager = function(){
        this.initTM = false;
    };
    TouchManager.prototype = {
        isLayouter: false,
        touchnames: "tap|doubletap|swipe|scroll|scrollstart|scrollend|touchstart|touchmove|touchend|rotate|pinch|spread|press",
        listeners: {},
        init:function(){
            this.initTM = true;
        },
        onTouchMove: function(e){e.preventDefault();},
        layoutMode: function(isLayouter){
            if(this.isLayouter && !isLayouter){
                document.removeEventListener('touchmove', this.onTouchMove, false);
            }
            else if(!this.isLayouter && isLayouter) document.addEventListener('touchmove', this.onTouchMove , false);
            this.isLayouter = isLayouter;
        },
        addListener: function(el, ev, callback, stopPropagation){
            if(!this.initTM) this.init();
            var id = undefined;
            if(typeof el === "string") id = el;
            else if(tactile.c.E.isHTMLElement(el)) id = el.id;
            else if(el instanceof tactile.foundation.Element) id = el.elem.id;
            if(id){
                elem = document.getElementById(id);
            } else {
                id = 'autoid_' + Math.random().toString().replace('.','');
                elem = el;
                el.id = id;
            }
            var evObj = tactile.events.EventsFactory.getEvent(ev, elem, callback, stopPropagation);
            this.listeners[id] = this.listeners[id] || {};
            this.listeners[id][ev] = this.listeners[id][ev] || [];
            this.listeners[id][ev].push(evObj);
        },
        removeListener: function(el, ev, callback){
        	var id = undefined;
            if(typeof el === "string")id = el;
            else if(tactile.c.E.isHTMLElement(el) || el instanceof HTMLDocument) id = el.id;
            else if(el instanceof tactile.foundation.Element) id = el.elem.id;
            callback = callback.toString().replace(/[\s\t\n]+/g, '');
            if(this.listeners[id] && this.listeners[id][ev]){
                for(var i = 0; i < this.listeners[id][ev].length; i++){
                    if(this.listeners[id][ev][i].callback.toString().replace(/[\s\t\n]+/g, '') == callback){
                        this.clearListener(id, ev, i);
                    }
                }
            }
        },
        removeAllListeners: function(el){
        	var id = undefined;
            if(typeof el === "string") id = el;
            else if(tactile.c.E.isHTMLElement(el)) id = el.id;
            else if(el instanceof tactile.foundation.Element) id = el.elem.id;
            if(this.listeners[id]){
                for(var ev in this.listeners[id]){
                    for(var i = 0; i < this.listeners[id][ev].length; i++){
                        this.clearListener(id, ev, i);
                    }
                }
            }
        },
        clearListener: function(id, ev, num){
            this.listeners[id][ev][num].removeEvent();
            this.listeners[id][ev][num] = null;
            this.listeners[id][ev].splice(num, 1);
        },
        getListenersByElement: function(el){
            var id = el.id;
            if(this.listeners && this.listeners[id]){
                return this.listeners[id];
            }
        },
        isTouchEvent : function(ev) {
            return (~this.touchnames.indexOf(ev) ? true : false);
        }
    };
    return new TouchManager();
})();
tactile.events.Event = function(element, callback, stopPropagation){};
tactile.events.Event.prototype = {
    eventType: null,
    eventName: 'TactileEvent',
    init: function(element, callback, stopPropagation){
        if(!element && !callback) throw "Error";
        this.element = element;
        this.callback = callback;
        this.stopPropagation = stopPropagation;
        this.getDeviceEvent();
        this.setup();
    },
    setup: function(){},
    onTouchStart: null,
    onTouchMove: null,
    onTouchEnd: null,
    normalizeEvent: function(e, properties){
        if(!tactile.Device.isTouch) e.touches = e.changedTouches = [e];
        var ev = {
            timeStamp: e.timeStamp.getTime ? e.timeStamp.getTime() : (e.timeStamp.toString().length > 13 ? e.timeStamp/1000 :  e.timeStamp),
            touches: e.touches,
            changedTouches: e.changedTouches,
            target: e.target,
            currentTarget: e.currentTarget,
            browserEvent: e,
            type: this.eventName
        };
        if(properties && typeof properties == 'object'){
            for(var key in properties){
                ev[key] = properties[key];
            }
        } else {
            if(e.touches.length){
                ev.pageX = e.touches[0].pageX;
                ev.pageY = e.touches[0].pageY;
            }
        }
        return ev;
    },
    removeEvent: function(){
        if(this.onTouchStart != null) this.element.removeEventListener(this.eventType.start, this.onTouchStart, false);
        if(this.onTouchMove != null) this.element.removeEventListener(this.eventType.move, this.onTouchMove, false);
        if(this.onTouchEnd != null) this.element.removeEventListener(this.eventType.end, this.onTouchEnd, false);
    },
    getDeviceEvent: function(){
        if(tactile.Device.isTouch)
            this.eventType = {
                'start': 'touchstart',
                'move': 'touchmove',
                'end': 'touchend'
            };
        else
            this.eventType = {
                'start': 'mousedown',
                'move': 'mousemove',
                'end': 'mouseup'
            };
    }
};
tactile.events.Touchstart = function(){
    this.init.apply(this, arguments);
};
tactile.events.Touchstart.prototype = {
    eventName: 'touchstart',
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.callback.call(this, self.normalizeEvent(e));
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    }
};
tactile.c.U.extend(tactile.events.Touchstart, tactile.events.Event);
tactile.events.Touchmove = function(){
    this.init.apply(this, arguments);
};
tactile.events.Touchmove.prototype = {
    eventName: 'touchmove',
    setup: function(){
        var self = this;
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.callback.call(this, self.normalizeEvent(e));
        };
        this.element.addEventListener(this.eventType.move, this.onTouchMove, false);
    }
};
tactile.c.U.extend(tactile.events.Touchmove, tactile.events.Event);
tactile.events.Touchend = function(){
    this.init.apply(this, arguments);
};
tactile.events.Touchend.prototype = {
    eventName: 'touchend',
    setup: function(){
        var self = this;
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.callback.call(this, self.normalizeEvent(e));
        };
        this.element.addEventListener(this.eventType.end, this.onTouchEnd, false);
    }
};
tactile.c.U.extend(tactile.events.Touchend, tactile.events.Event);
tactile.events.Scrollstart = function(){
    this.init.apply(this, arguments);
};
tactile.events.Scrollstart.prototype = {
    eventName: 'ScrollStart',
    eventAttached: false,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            this.addEventListener(self.eventType.move, self.onTouchMove, false);
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            this.scrollstart = true;
            self.callback.call(this, self.normalizeEvent(e));
            this.removeEventListener(self.eventType.move, self.onTouchMove, false);
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    },
    normalizeEvent: function(e){
        return tactile.events.Scrollstart.superclass.normalizeEvent.call(this, e);
    }
};
tactile.c.U.extend(tactile.events.Scrollstart, tactile.events.Event);
tactile.events.Scrollend = function(){
    this.init.apply(this, arguments);
};
tactile.events.Scrollend.prototype = {
    eventName: 'scrollend',
    isScrollStarted: false,
    startPosition: null,
    movingDelta: 15,
    eventData: null,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            this.addEventListener(self.eventType.move, self.onTouchMove, false);
            document.addEventListener(self.eventType.end, self.onTouchEnd, false);
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            if(this.scrollstart) self.generateScrollEvent(e);
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            if(self.element.scrollstart) self.callback.call(self.element, self.normalizeEvent(e));
            self.eventData = null;
            self.element.removeEventListener(self.eventType.move, self.onTouchStart, false);
            this.removeEventListener(self.eventType.end, self.onTouchEnd, false);
            self.element.scrollstart = null;
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    },
    generateScrollEvent: function(e){
        e = this.normalizeEvent.call(this, e);
        if(!this.eventData) this.eventData = {};
        this.eventData = {
            startX: this.eventData.startX ? this.eventData.startX : e.touches[0].pageX,
            startY: this.eventData.startY ? this.eventData.startY : e.touches[0].pageY,
            deltaX: this.eventData.startX ? (function(obj){
                return (obj.startX - e.touches[0].pageX)
            })(this.eventData) : 0,
            deltaY: this.eventData.startY ? this.eventData.startY - e.touches[0].pageY : 0,
            pageX: e.touches[0].pageX,
            pageY: e.touches[0].pageY,
            startTime: (this.eventData.startTime ? this.eventData.startTime : e.timeStamp),
            deltaTime: (this.eventData.startTime ? e.timeStamp - this.eventData.startTime : 0)
        };
    },
    normalizeEvent: function(e){
        return tactile.events.Scrollend.superclass.normalizeEvent.call(this, e, this.eventData);
    }
};
tactile.c.U.extend(tactile.events.Scrollend, tactile.events.Event);
tactile.events.Scroll = function(){
    this.init.apply(this, arguments);
};
tactile.events.Scroll.prototype = {
    eventName: 'scroll',
    eventData: null,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            this.addEventListener(self.eventType.move, self.onTouchMove, false);
            document.addEventListener(self.eventType.end, self.onTouchEnd, false);
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            if(this.scrollstart){
            	self.callback.call(this, self.normalizeEvent(e));
            }
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.element.removeEventListener(self.eventType.move, self.onTouchStart, false);
            this.removeEventListener(self.eventType.end, self.onTouchEnd, false);
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    },
    normalizeEvent: function(e){
        return tactile.events.Scrollend.superclass.normalizeEvent.call(this, e);
    }
};
tactile.c.U.extend(tactile.events.Scroll, tactile.events.Event);
tactile.events.Swipe = function(){
    this.init.apply(this, arguments);
};
tactile.events.Swipe.prototype = {
    eventName: 'swipe',
    eventData: null,
    swipeStarted: false,
    swipeMotionDelta: 100,
    swipeDeltaTime: 200,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            this.addEventListener(self.eventType.move, self.onTouchMove, false);
            this.addEventListener(self.eventType.end, self.onTouchEnd, false);
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            if(!self.swipeStarted) self.eventData = {};
            self.generateSwipeEvent(e);
            if(self.eventData.deltaTime < self.swipeDeltaTime && (self.eventData.deltaX >= self.swipeMotionDelta || self.eventData.deltaY >= self.swipeMotionDelta)){
            	self.callback.call(this, self.normalizeEvent(e));
            	self.onTouchEnd.call(this, e);
            }
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.reset();
            this.removeEventListener(self.eventType.move, self.onTouchMove, false);
            this.removeEventListener(self.eventType.end, self.onTouchEnd, false);
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    },
    reset: function(){
        this.eventData = null;
        this.swipeStarted = false;
    },
    generateSwipeEvent: function(e){
        e = this.normalizeEvent.call(this, e);
        this.eventData = {
            startX: this.eventData.startX ? this.eventData.startX : e.touches[0].pageX,
            startY: this.eventData.startY ? this.eventData.startY : e.touches[0].pageY,
            deltaX: this.eventData.startX ? (function(obj){
                return Math.abs(obj.startX - e.touches[0].pageX);
            })(this.eventData) : 0,
            deltaY: this.eventData.startY ? Math.abs(this.eventData.startY - e.touches[0].pageY) : 0,
            pageX: e.touches[0].pageX,
            pageY: e.touches[0].pageY,
            startTime: (this.eventData.startTime ? this.eventData.startTime : e.timeStamp),
            deltaTime: (this.eventData.startTime ? e.timeStamp - this.eventData.startTime : 0)
        };
        this.swipeStarted = true;
    },
    getSwipeDirection: function(e){
        var direction;
        if(this.eventData.deltaX < this.eventData.deltaY){
            direction = this.eventData.startY > e.touches[0].pageY ? 'top' : 'bottom';
        } else {
            direction = this.eventData.startX > e.touches[0].pageX ? 'left' : 'right';
        }
        return direction;
    },
    normalizeEvent: function(e){
        e = tactile.events.Swipe.superclass.normalizeEvent.call(this, e);
        e['direction'] = this.getSwipeDirection(e);
        return e;
    }
};
tactile.c.U.extend(tactile.events.Swipe, tactile.events.Event);
tactile.events.Tap = function(){
    this.init.apply(this, arguments);
};
tactile.events.Tap.prototype = {
    eventName: 'tap',
    timeDelta: 300,
    movingDelta: 10,
    startTime: null,
    startPosition: null,
    isTap: true,
    eventAttached: false,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            self.isTap = true;
            self.startTime = e.timeStamp;
            self.startPosition = {
                x: e.touches[0].pageX,
                y: e.touches[0].pageY
            };
            if(!self.eventAttached){
                this.addEventListener(self.eventType.move, self.onTouchMove, false);
                this.addEventListener(self.eventType.end, self.onTouchEnd, false);
                self.eventAttached = true;
            }
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            self.isTap = (Math.abs(self.startPosition.x - e.touches[0].pageX) <= self.movingDelta || Math.abs(self.startPosition.y - e.touches[0].pageY) <= self.movingDelta);
        };
        this.onTouchEnd = function(e){
    		if(self.stopPropagation) e.stopPropagation();
            if(!self.isTap) return false;
            e = self.normalizeEvent(e)
            var currentTime = e.timeStamp - self.startTime;
            if(currentTime <= self.timeDelta){
            	self.callback.call(this, e);
            }
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    }
};
tactile.c.U.extend(tactile.events.Tap, tactile.events.Event);
tactile.events.Doubletap = function(){
    this.init.apply(this, arguments);
};
tactile.events.Doubletap.prototype = {
    eventName: 'doubletap',
    timeDelta: 300,
    startTime: null,
    isDoubleStart: false,
    eventAttached: false,
    setup: function(){
    	var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            var currentTime;
            if(self.startTime){
                currentTime = e.timeStamp - self.startTime;
                if(currentTime >= self.timeDelta) self.isDoubleStart = false;
            }
            if(!self.isDoubleStart){
                self.startTime = e.timeStamp;
                this.addEventListener(self.eventType.end, self.onTouchEnd, false);
            } else {
            	currentTime = e.timeStamp - self.startTime;
                if(currentTime <= self.timeDelta){
                	self.callback.call(this, e);
                }
                self.startTime = null;
            }
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            if(self.isDoubleStart){
                this.removeEventListener(self.eventType.move, self.onTouchMove, false);
                this.removeEventListener(self.eventType.end, self.onTouchEnd, false);
                self.isDoubleStart = false;
            } else {
                self.isDoubleStart = true;
            }
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    }
};
tactile.c.U.extend(tactile.events.Doubletap, tactile.events.Event);
tactile.events.Press = function(){
    this.init.apply(this, arguments);
};
tactile.events.Press.prototype = {
    eventName: 'press',
    timeDelta: 600,
    movingDelta: 10,
    startTime: null,
    startPosition: null,
    isPress: true,
    timer: null,
    eventAttached: false,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            self.isPress = true;
            self.startTime = e.timeStamp;
            self.startPosition = {
                x: e.touches[0].pageX,
                y: e.touches[0].pageY
            };
            if(!self.eventAttached){
                this.addEventListener(self.eventType.move, self.onTouchMove, false);
                this.addEventListener(self.eventType.end, self.onTouchEnd, false);
                self.eventAttached = true;
            }
            self.timer = setTimeout(function(){
            	self.callback.call(self.element, self.normalizeEvent(e));
                clearTimeout(self.timer);
            }, self.timeDelta);
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            if((Math.abs(self.startPosition.x - e.touches[0].pageX) > self.movingDelta || Math.abs(self.startPosition.y - e.touches[0].pageY) > self.movingDelta)){
                clearTimeout(self.timer);
            }
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            clearTimeout(self.timer);
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    }
};
tactile.c.U.extend(tactile.events.Press, tactile.events.Event);
tactile.events.Gesture = function(){
    this.init.apply(this, arguments);
};
tactile.events.Gesture.prototype = {
    eventName: 'gesture',
    eventAttached: false,
    touchDistance: null,
    setup: function(){
        var self = this;
        this.onTouchStart = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            self.setDefaultData(e);
            if(!self.eventAttached){
                this.addEventListener(self.eventType.move, self.onTouchMove, false);
                this.addEventListener(self.eventType.end, self.onTouchEnd, false);
                self.eventAttached = true;
            }
        };
        this.onTouchMove = function(e){
            if(self.stopPropagation) e.stopPropagation();
            e = self.normalizeEvent(e);
            self.calculateEvent(e);
        };
        this.onTouchEnd = function(e){
            if(self.stopPropagation) e.stopPropagation();
            self.resetData();
        };
        this.element.addEventListener(this.eventType.start, this.onTouchStart, false);
    },
    setDefaultData: function(e){},
    resetData: function(){},
    calculateEvent: function(e){},
    normalizeEvent: function(e, eventObject){
        e = tactile.events.Swipe.superclass.normalizeEvent.call(this, e);
        for(var i in eventObject){
            e[i] = eventObject[i];
        }
        return e;
    }
};
tactile.c.U.extend(tactile.events.Gesture, tactile.events.Event);
tactile.events.Pinch = function(){
    this.init.apply(this, arguments);
};
tactile.events.Pinch.prototype = {
    eventName: 'pinch',
    touchDistance: null,
    setDefaultData: function(e){
    	if(e.touches.length == 2){
            this.gestureDeltaTime = e.timeStamp;
            if(!this.element.$scale) this.element.$scale = 1;
        }
    },
    resetData: function(){
        this.touchDistance = null;
        this.gestureDeltaTime = 0;
    },
    calculateEvent: function(e){
    	if(e.touches.length != 2) return false;
        if(!this.touchDistance) this.touchDistance = Math.abs(e.touches[0].pageX - e.touches[1].pageX) + Math.abs(e.touches[0].pageY - e.touches[1].pageY);
        var endDistance = Math.abs(e.touches[0].pageX - e.touches[1].pageX) + Math.abs(e.touches[0].pageY - e.touches[1].pageY),
            deltaTime = e.timeStamp - this.gestureDeltaTime;
        if(this.checkCondition(this.touchDistance, endDistance)){
            this.element.$scale = (endDistance * this.element.$scale)/this.touchDistance;
            this.callback.call(this.element, this.normalizeEvent(e, {
                'scale' : this.element.$scale,
                'deltaTime' : deltaTime,
                'distance' : endDistance
            }));
        }
        this.touchDistance = endDistance;
    },
    checkCondition: function(start, end){
        return start > end;
    }
};
tactile.c.U.extend(tactile.events.Pinch, tactile.events.Gesture);
tactile.events.Spread = function(){
    this.init.apply(this, arguments);
};
tactile.events.Spread.prototype = {
    eventName: 'spread',
    checkCondition: function(start, end){
        return start < end;
    }
};
tactile.c.U.extend(tactile.events.Spread, tactile.events.Pinch);
tactile.events.Rotate = function(){
    this.init.apply(this, arguments);
};
tactile.events.Rotate.prototype = {
    eventName: 'rotate',
    touchDistance: null,
    setDefaultData: function(e){
    	if(e.touches.length == 2){
            this.gestureDeltaTime = e.timeStamp;
            this.startData = [];
            for(var i=0; i < e.touches.length; i++){
                this.startData.push({
                    x: e.touches[i].pageX,
                    y: e.touches[i].pageY
                });
            }
            if(!this.element.$rotation) this.element.$rotation = 0;
        }
    },
    resetData: function(){
    	this.touchDistance = null;
        this.gestureDeltaTime = 0;
        this.element.$rotation = this.endAng;
    },
    calculateEvent: function(e){
    	if(e.touches.length != 2) return false;
        var ang1 = this.calculateAngle(this.startData[0],this.startData[1]),
        t1 = {
            x: e.touches[0].pageX,
            y: e.touches[0].pageY
        },
        t2 = {
            x: e.touches[1].pageX,
            y: e.touches[1].pageY
        },
        ang2 = this.calculateAngle(t1, t2),
        deltaTime = e.timeStamp - this.gestureDeltaTime;
        this.endAng = this.element.$rotation + (ang1 - ang2);
        this.callback.call(this.element, this.normalizeEvent(e, {
            rotation: this.endAng,
            deltaTime : deltaTime
        }));
    },
    calculateAngle: function(m, t){
        var b = Math.abs(m.x - t.x),
            a = Math.abs(m.y - t.y),
            c = Math.sqrt((a*a) + (b*b)),
            alpha = Math.asin(a/c),
            deg = alpha*(180/Math.PI);
        if(m.x > t.x && m.y < t.y) deg = -((90 - deg) + 90);
        if(m.x > t.x && m.y > t.y) deg = (90 - deg)+90;
        if(m.x < t.x && m.y < t.y) deg = -deg;
        return Math.ceil(deg);
    }
};
tactile.c.U.extend(tactile.events.Rotate, tactile.events.Gesture);
tactile.Enum = {
	Direction : {
		north: "north",
		east: "east",
		south: "south",
		west: "west"
	},
	Display : {
		block : "block",
		inline : "inline",
		none : "none",
		inherit : "inherit"
	},
	Effects : {
		slide: "slide",
		fade: "fade",
		expand: "expand"
	},
	FlexFlow : {
		horizontal : "horizontal",
		vertical : "vertical"
	},
	GridFlow : {
		horizontal : "horizontal",
		vertical : "vertical"
	},
	Dimension : {
		width: "width",
		height: "height"
	},
	Float : {
		left : "left",
		right : "right",
		none : "none",
		inherit : "inherit"
	},
	Overflow : {
		visible : "visible",
		hidden : "hidden",
		scroll : "scroll",
		inherit : "inherit"
	},
	Position : {
		"static" : "static",
		absolute : "absolute",
		relative : "relative",
		inherit : "inherit",
		fixed : "fixed"
	},
	Scroll : {
		horizontal : "horizontal",
		vertical : "vertical",
		both : "both",
		disabled: "disabled"
	},
	Transitions : {
		linear: "linear",
		easeIn: "ease-in",
		easeOut: "ease-out",
		easeInOut: "ease-in-out",
		easeOutIn: "ease-out-in"
	},
	Unit : {
		percent : "%",
		px : "px",
		em : "em"
	},
	Events : {
		touchstart: "touchstart",
		touchmove: "touchmove",
		touchend: "touchend",
		scrollstart: "scrollstart",
		scroll: "scroll",
		scrollend: "scrollend",
		tap: "tap",
		doubletap: "doubletap",
		swipe: "swipe"
	},
	contains : function(prop, value) {
		if (value === undefined || value === null) {
			return false;
		}
		var found = false;
		var v = value.toLowerCase();
		for (var x in prop) {
			if (prop[x] == v) {
				found = true;
				break;
			}
		}
		return found;
	},
	getValue : function(prop, value) {
		if (value === undefined) {
			return "";
		}
		var v = value.toLowerCase();
		for (var x in prop) {
			if (prop[x] == v) {
				return prop[x];
				break;
			}
		}
		return "";
	},
	getKey : function(prop, value) {
		if (value === undefined) {
			return "";
		}
		var v = value.toLowerCase();
		for (var x in prop) {
			if (prop[x] == v) {
				return x;
				break;
			}
		}
		return "";
	},
	getKey : function(prop, value) {
		if (value === undefined) {
			return "";
		}
		var v = value.toLowerCase();
		for (var x in prop) {
			if (prop[x] == v) {
				return x;
				break;
			}
		}
		return "";
	}
};
tactile.effects.Operations = {
		show: "show",
		hide: "hide",
		move: "move",
		none: "none"
};
tactile.effects.Properties = {
		left: "left",
		top: "top",
		width: "width",
		height: "height"
};
tactile.effects.Method = {
		javascript: "javascript",
		transition: "transition",
		transform: "transform",
		transform3d: "transform3d",
		disabled: "disabled"
};
tactile.effects.Direction = {
		horizontal: "horizontal",
		vertical: "vertical"
};
tactile.effects.Transitions = {
		linear: function(pos){
			return pos;
		},
		inQuad: function(pos){
			return Math.pow(pos, 2);
		},
		inCubic: function(pos){
			return Math.pow(pos, 3);
		},
		outQuad: function(pos){
			return 1 - Math.pow((pos - 1), 2);
		},
		outCubic: function(pos){
			return 1 + Math.pow((pos - 1), 3);
		},
		inOutQuad: function(pos){
			return 2 * Math.pow(pos, 3) + 3 * Math.pow(pos, 2);
		},
		inCirc: function(pos){
			return 1 - Math.sqrt(1 - Math.pow(pos, 2));
		},
		outCirc: function(pos){
			return Math.sqrt(pos);
		},
		inElastic: function(pos){
			return Math.pow(pos, 4) * Math.sin(pos * Math.PI * 6.5);
		},
		outElastic: function(pos){
			return 1 - Math.pow(pos - 1, 4) * Math.sin((1 - pos) * Math.PI * 6.5);
		},
		sinoidal: function(pos){
			return (-Math.cos(pos*Math.PI)/2) + .5;
		},
		cosinus: function(pos){
			return Math.cos(pos);
		},
		decel:function(pos) {
			return -Math.pow(pos,2) + 2*pos;
		}
};
tactile.effects.Transitions.easeIn = tactile.effects.Transitions.outCubic;
tactile.effects.Transitions.easeOut = tactile.effects.Transitions.sinoidal;
tactile.effects.Transitions.easeInOut = tactile.effects.Transitions.cosinus;
tactile.effects.Transitions.easeOutIn = tactile.effects.Transitions.decel;
tactile.effects.Timing = {
	linear: "linear",
	easeIn: "ease-in",
	easeOut: "ease-out",
	easeInOut: "ease-in-out",
	easeOutIn: "ease-out-in"
};
tactile.effects.Base = new Function();
tactile.effects.Base.prototype = {
		offsetInitialized: false,
		leftOffset: null,
		topOffset: null,
		timer: null,
		from: null,
		to: null,
		prop: null,
		start: null,
		endEventAdded: false,
		op: tactile.effects.Operations.none,
		init: function(container, config) {
			if (!container instanceof tactile.foundation.UIContainer) {
				throw tactile.errors.TypeError("tactile.effects.Slide","UIComponent",typeof container);
			}
			this.inst = container;
			config = config || {};
			this.duration = config.duration || tactile.Device.defaults.effectduration;
			this.useTransition = tactile.Device.browser.csstransitions;
			this.useTransform = tactile.Device.browser.csstransforms;
			this.useTransform3d = tactile.Device.browser.csstransforms3d;
			this.fps = 20;
			if(typeof nbdevice == "object"){
				if(this instanceof tactile.effects.Slide){
					if(nbdevice.tactileslidemethod){
						var method = nbdevice.tactileslidemethod.toLowerCase();
						this.useTransform3d = (method == tactile.effects.Method.transform3d);
						this.useTransform = (method == tactile.effects.Method.transform);
						this.useTransition = (method == tactile.effects.Method.transition);
						if(method == tactile.effects.Method.javascript && nbdevice.tactileslidefps){
							this.fps = Number(nbdevice.tactileslidefps);
						}
					}
				}else if(this instanceof tactile.effects.Fade){
					if(nbdevice.tactilefademethod){
						var method = nbdevice.tactilefademethod.toLowerCase();
						this.useTransform3d = (method == tactile.effects.Method.transform3d);
						this.useTransform = (method == tactile.effects.Method.transform);
						this.useTransition = (method == tactile.effects.Method.transition);
						if(method == tactile.effects.Method.javascript && nbdevice.tactilefadefps){
							this.fps = Number(nbdevice.tactilefadefps);
						}
					}
				} else if(this instanceof tactile.effects.Expand){
					if(nbdevice.tactileexpandmethod){
						var method = nbdevice.tactileexpandmethod.toLowerCase();
						this.useTransform3d = (method == tactile.effects.Method.transform3d);
						this.useTransform = (method == tactile.effects.Method.transform);
						this.useTransition = (method == tactile.effects.Method.transition);
						if(method == tactile.effects.Method.javascript && nbdevice.tactileexpandfps){
							this.fps = Number(nbdevice.tactileexpandfps);
						}
					}
				}
			}
			if(config.fps){
				this.fps = config.fps;
			}
			if (config.method && config.method == tactile.effects.Method.javascript) {
				this.useTransition = false;
				this.useTransform = false;
				this.useTransform3d = false;
			}
			if (config.method && config.method == tactile.effects.Method.transition) {
				this.useTransform = false;
				this.useTransition = true;
				this.useTransform3d = false;
			}
			if (config.method && config.method == tactile.effects.Method.transform) {
				this.useTransition = false;
				this.useTransform = true;
				this.useTransform3d = false;
			}
			if (config.method && config.method == tactile.effects.Method.transform3d) {
				this.useTransition = false;
				this.useTransform = false;
				this.useTransform3d = true;
			}
			if(this instanceof tactile.effects.Fade || this instanceof tactile.effects.Expand){
				this.useTransform3d = false;
			}
			if (this.useTransition || this.useTransform || this.useTransform3d) {
				this.timing = tactile.effects.Timing.easeOut;
				if (config.transition && tactile.Enum.contains(tactile.Enum.Transitions, config.transition)) {
					this.timing = tactile.Enum.getValue(tactile.Enum.Transitions, config.transition);
				}
			} else {
				this.timing = tactile.effects.Transitions.outCubic;
				if (config.transition && tactile.Enum.contains(tactile.Enum.Transitions, config.transition)) {
					this.timing = tactile.effects.Transitions[tactile.Enum.getKey(tactile.Enum.Transitions, config.transition)];
				}
			}
			if(this.useTransform || this.useTransform3d) {
				if (this.inst instanceof tactile.FlexView) {
					this.inst.flexitems.forEach( function(el) {	tactile.c.U.applyTransformFlickerFix(el.elem);});
				}else{
					tactile.c.U.applyTransformFlickerFix(this.inst.elem);
				}
			}
			this.setOffset();
		},
		getMethod: function(){
			if(this.useTransform3d){
				return tactile.effects.Method.transform3d;
			}else if(this.useTransform){
				return tactile.effects.Method.transform;
			}else if(this.useTransition){
				return tactile.effects.Method.transition;
			}else{
				return tactile.effects.Method.javascript;
			}
		},
		setOffset: function(){
			if(this.inst.parent){
				this.leftOffset = (typeof this.inst.configLeft === "function") ? this.inst.configLeft(this.inst.getParentInnerBox()) : 0;
				this.topOffset = (typeof this.inst.configTop === "function") ? this.inst.configTop(this.inst.getParentInnerBox()) : 0;
				this.offsetInitialized = true;
			}
		},
		step: function() {
			var time  = (new Date).getTime();
			if(time < this.start + this.duration) {
				var deltaTime = (time - this.start) / this.duration;
				var deltaPos = Math.round(this.from + (this.to - this.from) * this.timing(deltaTime));
				this.jsstep(deltaPos);
			} else {
				clearInterval(this.timer);
				this.timer = null;
				if (this.op == tactile.effects.Operations.hide) {
					this.inst.elem.style.display = tactile.Enum.Display.none;
					if (this.inst.afterhide) {
						this.inst.afterhide.fire({targetId:this.inst.id});
					}
				} else if (this.op == tactile.effects.Operations.show) {
					if (this.inst.aftershow) {
						this.inst.aftershow.fire({targetId:this.inst.id});
					}
					if(this instanceof tactile.effects.Expand){
						this.inst.elem.style[this.prop] = "";
					}
				}
				if (this.onend) {
					this.onend.fire({operation:this.op});
				}
				this.op = tactile.effects.Operations.none;
			}
		},
		setEndEvent: function() {
			if (this.endEventAdded) {
				return;
			}
			if (!tactile.Device.browser.transitionEndEventSupported() || tactile.c.U.iOS3()) {
				setTimeout(this.transitionEnd.bind(this), this.duration+200);
			} else {
				if (!this.endEventAdded) {
					tactile.c.V.addManagedListener(this.inst.elem, tactile.Device.browser.transitionEndEventType(), this.transitionEnd, this);
					this.endEventAdded = true;
				}
			}
		},
		transitionEnd: function(e) {
		}
	};
tactile.effects.Slide = function(container, config) {
	this.init(container, config);
};
tactile.effects.Slide.prototype = {
	init: function(){
		tactile.effects.Slide.superclass.init.apply(this, arguments);
		this.jsstep = function(delta) { this.inst.elem.style[this.prop] = delta + tactile.Enum.Unit.px; };
		this.onend = new tactile.foundation.Observer();
		this.scroll = {x:0,y:0};
		this.posX = 0;
		this.posY = 0;
		this.transform = {x:0,y:0};
		var transStyle = this.inst.elem.style[tactile.Device.browser.style.transformProperty];
		if(transStyle){
			transStyle = transStyle.replace(/px/g,"");
			var styles = (transStyle.substring(transStyle.indexOf("(")+1,transStyle.indexOf(")"))).split(",");
			this.transform = {x:Number(styles[0]),y:Number(styles[1])};
		}
	},
	transitionEnd: function(e) {
		var bs = tactile.Device.browser.style, op = tactile.effects.Operations;
		if (this.op == op.none) {
			return false;
		}
		if (this.useTransform || this.useTransform3d) {
			if (this.op == op.move) {
				tactile.c.U.updateRenderTreeFix(this.inst.elem);
			}
			if(this.op == op.hide){
				if(this.inst.scroller){
				}
			}
		} else if (this.useTransition) {
			this.inst.elem.style[bs.transitionProperty] = "";
			this.inst.elem.style[bs.transitionDuration] = "";
			this.inst.elem.style[bs.transitionTimingFunction] = "";
			this.inst.elem.style[this.prop] = this.to + tactile.Enum.Unit.px;
		}
		if (this.op == op.hide) {
			this.inst.elem.style.display = tactile.Enum.Display.none;
			if (this.inst.afterhide) {
				this.inst.afterhide.fire({targetId:this.inst.id});
			}
		} else if (this.op == op.show) {
			if (this.inst.aftershow) {
				this.inst.aftershow.fire({targetId:this.inst.id});
			}
		}
		if (this.op == op.show) {
			if((tactile.Device.platform === "ios" && tactile.Device.version >= 5) || (tactile.Device.platform === "android" && tactile.Device.version >= 4)){
				window.setTimeout(function(){
					tactile.c.U.updateRenderTreeFix(this.inst.elem);
					if(tactile.Device.platform === "android" && tactile.Device.version == 4){
						window.setTimeout(function(){
							tactile.c.U.updateRenderTreeFix(this.inst.elem);
							window.setTimeout(function(){
								tactile.c.U.updateRenderTreeFix(this.inst.elem);
								window.setTimeout(function(){
									tactile.c.U.updateRenderTreeFix(this.inst.elem);
								}.bind(this),150);
							}.bind(this),150);
						}.bind(this),150);
					}
				}.bind(this),50);
			}
		}
		this.onend.fire({operation: this.op});
		this.op = op.none;
	},
	show: function(config) {
		this.setOffset();
		config = config || {};
		var bs = tactile.Device.browser.style,
		op = tactile.effects.Operations,
		slidefrom = config.slidefrom || tactile.Enum.Direction.east,resetprop;
		if (this.op != op.none) {
			return false;
		}
		if (slidefrom == tactile.Enum.Direction.east || slidefrom == tactile.Enum.Direction.west) {
			this.prop = tactile.effects.Properties.left;
			resetprop = tactile.effects.Properties.top;
			if (slidefrom == tactile.Enum.Direction.east) {
				this.from = this.inst.getParentInnerBox().width  + 2*this.leftOffset;
			} else {
				this.from = -this.inst.getParentInnerBox().width;
			}
			var nl = this.inst.validateNumber(this.inst.configLeft, this.inst.getOuterBox());
			if (nl) {
				this.to = nl;
			} else {
				this.to = this.inst.getParentInnerBox().left;
			}
			this.posY = 0;
		} else {
			this.prop = tactile.effects.Properties.top;
			resetprop = tactile.effects.Properties.left;
			if (slidefrom == tactile.Enum.Direction.north) {
				this.from = - this.inst.getParentInnerBox().height;
			} else {
				this.from = this.inst.getParentInnerBox().height;
			}
			var nt = this.inst.validateNumber(this.inst.configTop, this.inst.getParentInnerBox());
			if (nt) {
				this.to = nt;
			} else {
				this.to = this.inst.getParentInnerBox().top;
			}
			this.posX = 0;
		}
		if (this.useTransform || this.useTransform3d){
			this.inst.elem.style[bs.transitionProperty] = "";
			this.inst.elem.style[bs.transformProperty] = "";
			this.inst.elem.style[bs.transitionDuration] = "";
			this.inst.elem.style[bs.transitionTimingFunction] = "";
			this.op = op.show;
			this.inst.setOpacity(0);
			this.inst.elem.style[resetprop] = (resetprop == tactile.effects.Properties.top) ? this.topOffset+"px" : this.leftOffset+"px";
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.elem.style.display = this.inst.elem.displayStyle;
			this.inst.update(this.inst.getParentInnerBox());
			this.inst.elem.style[bs.transitionProperty] = tactile.Device.browser.prefixed("transform");
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.setOpacity(100);
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			if (slidefrom == tactile.Enum.Direction.east || slidefrom == tactile.Enum.Direction.west) {
				var distance = this.from ;
				this.posX = - distance + this.leftOffset;
				if(this.useTransform3d){
					this.inst.elem.style[bs.transformProperty] = "translate3d(" + this.posX + "px,"+this.posY+"px,0px)";
				}else{
					this.inst.elem.style[bs.transformProperty] = "translate(" + this.posX + "px,"+this.posY+"px)";
				}
			} else {
				var distance = this.from;
				this.posY = - distance + this.topOffset;
				if(this.useTransform3d){
					this.inst.elem.style[bs.transformProperty] = "translate3d("+this.posX+"px," + this.posY  + "px,0px)";
				}else{
					this.inst.elem.style[bs.transformProperty] = "translate("+this.posX+"px," + this.posY  + "px)";
				}
			}
			this.setEndEvent();
		} else if (this.useTransition) {
			this.op = op.show;
			this.inst.elem.style[bs.transitionProperty] = this.prop;
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.elem.style[resetprop] = (resetprop == tactile.effects.Properties.top) ? this.topOffset+"px" : this.leftOffset+"px";
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.elem.style.display = this.inst.elem.displayStyle;
			this.inst.update(this.inst.getParentInnerBox());
			this.inst.elem.style[this.prop] = this.to + tactile.Enum.Unit.px;
			this.setEndEvent();
		} else {
			this.inst.setOpacity(0);
			this.inst.elem.style.display = this.inst.elem.displayStyle;
			this.inst.update(this.inst.getParentInnerBox());
			this.inst.elem.style[resetprop] = (resetprop == tactile.effects.Properties.top) ? this.topOffset+"px" : this.leftOffset+"px";
			this.inst.elem.style[this.prop] = "";
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.setOpacity(100);
			this.op = op.show;
			this.start = (new Date).getTime();
			this.timer = setInterval(this.step.bind(this), 1000/this.fps);
		}
		tactile.logger.info(this.toString() + ", show: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
		return true;
	},
	hide: function(config) {
		if(!this.offsetInitialized){
			this.setOffset();
		}
		config = config || {};
		var bs = tactile.Device.browser.style,
		op = tactile.effects.Operations,
		slideto = config.slideto || tactile.Enum.Direction.west,
		nl;
		if (this.op != op.none) {
			return false;
		}
		if (slideto == tactile.Enum.Direction.east || slideto == tactile.Enum.Direction.west) {
			this.prop = tactile.effects.Properties.left;
			if (slideto == tactile.Enum.Direction.east) {
				this.to = this.inst.getParentBox().width;
			} else {
				this.to = -this.inst.getParentBox().width;
			}
			this.posX += this.to + this.transform.x;
			nl = this.inst.validateNumber(this.inst.configLeft, this.inst.getParentInnerBox());
			if (nl) {
				this.from = nl;
			} else {
				this.from = this.inst.getParentInnerBox().left;
			}
		} else {
			this.prop = tactile.effects.Properties.top;
			if (slideto == tactile.Enum.Direction.north) {
				this.to = - this.inst.getParentBox().height;
			} else {
				this.to = this.inst.getParentBox().height;
			}
			this.posY += this.to + this.transform.y;
			var nt = this.inst.validateNumber(this.inst.configTop, this.inst.getParentInnerBox());
			if (nt) {
				this.from = nt;
			} else {
				this.from = this.inst.getParentInnerBox().top;
			}
		}
		this.op = op.hide;
		var xPos,yPos;
		xPos = 0;
		yPos = 0;
		if (this.useTransform || this.useTransform3d) {
			this.inst.elem.style[bs.transitionProperty] = tactile.Device.browser.prefixed("transform");
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			var tmpto = this.to;
			if (slideto == tactile.Enum.Direction.east || slideto == tactile.Enum.Direction.west) {
				this.to = (slideto == tactile.Enum.Direction.east) ?  this.posX + this.leftOffset : this.posX - this.leftOffset;
				if(this.useTransform3d){
					this.inst.elem.style[bs.transformProperty] = "translate3d(" + this.to + "px,"+this.posY+"px,0px)";
				}else{
					this.inst.elem.style[bs.transformProperty] = "translate(" + this.to + "px,"+this.posY+"px)";
				}
			} else {
				this.to = (slideto == tactile.Enum.Direction.south) ?  this.posY + this.topOffset : this.posY - this.topOffset;
				if(this.useTransform3d){
					this.inst.elem.style[bs.transformProperty] = "translate3d("+this.posX+"px," + this.to + "px,0px)";
				}else{
					this.inst.elem.style[bs.transformProperty] = "translate("+this.posX+"px," + this.to + "px)";
				}
			}
			this.setEndEvent();
		} else if (this.useTransition) {
			this.to = (slideto == tactile.Enum.Direction.east) ?  this.to + 2*this.leftOffset : this.to;
			this.inst.elem.style[bs.transitionProperty] = this.prop;
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.elem.style[this.prop] = this.to + tactile.Enum.Unit.px;
			this.setEndEvent();
		} else {
			this.to = (slideto == tactile.Enum.Direction.east) ?  this.to + 2*this.leftOffset : this.to;
			this.start = (new Date).getTime();
			this.timer = setInterval(this.step.bind(this), this.duration/this.fps);
		}
		tactile.logger.info(this.toString() + ", hide: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
		return true;
	},
	move: function(config) {
		config = config || {};
		var bs = tactile.Device.browser.style,
		op = tactile.effects.Operations,
		dir = config.slidedir || tactile.effects.Direction.vertical;
		if (this.op != op.none) {
			return false;
		}
		var pibox = this.inst.getParentInnerBox();
		if (dir == tactile.effects.Direction.horizontal) {
			this.prop = tactile.effects.Properties.left;
			this.to = config.to || pibox.left;
			this.from = config.from || this.inst.getOuterBox().left;
		} else {
			this.prop = tactile.effects.Properties.top;
			this.to = config.to || pibox.top;
			this.from = config.from || this.inst.getOuterBox().top;
		}
		if (this.useTransform || this.useTransform3d) {
			this.op = op.move;
			this.inst.elem.style[bs.transitionProperty] = tactile.Device.browser.prefixed("transform");
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			var tmpto = this.to - this.from;
			var translatestring = "";
			if (dir == tactile.effects.Direction.horizontal) {
				translatestring = this.useTransform3d ? "translate3d(" + tmpto + "px, 0px, 0px)" : "translate(" + tmpto + "px, 0px)";
				this.inst.elem.style[bs.transformProperty] = translatestring;
			} else {
				translatestring = this.useTransform3d ? "translate3d(0px, " + tmpto + "px, 0px)" : "translate(0px, " + tmpto + "px)";
				this.inst.elem.style[bs.transformProperty] = "translate(0px, " + tmpto + "px)";
			}
			this.setEndEvent();
			if (this.inst instanceof tactile.FlexView) {
				this.inst.flexitems.forEach( function(el) {	tactile.c.U.applyTransformFlickerFix(el.elem);});
			}
		} else if (this.useTransition) {
			this.op = op.move;
			this.inst.elem.style[bs.transitionProperty] = this.prop;
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.elem.style[this.prop] = this.to + tactile.Enum.Unit.px;
			this.setEndEvent();
		} else {
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.op = op.move;
			this.start = (new Date).getTime();
			this.timer = setInterval(this.step.bind(this), 1000/this.fps);
		}
		tactile.logger.info(this.toString() + ", move: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
		return true;
	},
	toString: function() {
		var method = "javascript, Frames per sec.: " + this.fps;
		if (this.useTransition) {
			method = "transition";
		}
		if (this.useTransform) {
			method = "transform (2d)";
		}
		if (this.useTransform3d) {
			method = "transform 3d";
		}
		return "Object - tactile.effects.Slide: " + method;
	}
};
tactile.c.U.extend(tactile.effects.Slide, tactile.effects.Base);
tactile.effects.Expand = function(container, config) {
	this.init(container, config);
};
tactile.effects.Expand.prototype = {
	init: function(){
		tactile.effects.Expand.superclass.init.apply(this, arguments);
		this.jsstep = function(delta) {tactile.c.U.updateRenderTreeFix(this.inst.elem); this.inst.elem.style[this.prop] = delta + tactile.Enum.Unit.px; };
		this.onend = new tactile.foundation.Observer();
	},
	transitionEnd: function() {
		var bs = tactile.Device.browser.style, op = tactile.effects.Operations;
		if (this.op == op.hide) {
			this.inst.elem.style.display = tactile.Enum.Display.none;
			if (this.inst.afterhide) {
				this.inst.afterhide.fire({targetId:this.inst.id});
			}
		} else if (this.op == op.show) {
			if (this.inst.aftershow) {
				this.inst.aftershow.fire({targetId:this.inst.id});
			}
			this.inst.elem.style[this.prop] = "";
		}
		this.inst.elem.style[bs.transitionProperty] = "";
		this.inst.elem.style[bs.transitionDuration] = "";
		this.inst.elem.style[bs.transitionTimingFunction] = "";
		this.onend.fire({operation: this.op});
		if (this.op == op.show) {
			if(tactile.Device.platform === "ios" && tactile.Device.version >= 5){
				window.setTimeout(function(){
					tactile.c.U.updateRenderTreeFix(this.inst.elem);
				}.bind(this),50);
			}
		}
		this.op = op.none;
	},
	transitionStart: function() {
		var bs = tactile.Device.browser.style, self = this;
		this.inst.elem.style[bs.transitionProperty] = this.prop;
		this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
		this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
		window.setTimeout(function(){
			self.inst.elem.style[self.prop] = self.to + tactile.Enum.Unit.px;
			self.setEndEvent();
		}, 10);
	},
	show: function(config) {
		if (this.op != tactile.effects.Operations.none) {
			return false;
		}
		config = config || {};
		var dim = config.expand || tactile.Enum.Dimension.width;
		this.from = config.from || 0;
		if (dim == tactile.Enum.Dimension.width) {
			this.prop = tactile.effects.Properties.width;
			var nw = this.inst.validateNumber(this.inst.configWidth, this.inst.getParentInnerBox());
			if (nw) {
				this.to = nw;
			} else {
				this.inst.elem.style.display = this.inst.elem.displayStyle;
	            this.to = this.inst.elem.querySelector('div').offsetWidth;
	            this.inst.elem.style.display = tactile.Enum.Display.none;
			}
		} else {
			this.prop = tactile.effects.Properties.height;
			var nh = this.inst.validateNumber(this.inst.configHeight, this.inst.getParentInnerBox());
			if (nh) {
				this.to = nh;
			} else {
				this.inst.elem.style.display = this.inst.elem.displayStyle;
	            this.to = this.inst.elem.querySelector('div').offsetHeight;
	            this.inst.elem.style.display = tactile.Enum.Display.none;
			}
		}
		if (this.useTransition) {
			this.inst.setOpacity(0);
			this.inst.elem.style.display = this.inst.elem.displayStyle;
			this.inst.update(this.inst.getParentInnerBox());
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.setOpacity(100);
			this.layoutinit = true;
			this.op = tactile.effects.Operations.show;
			setTimeout(this.transitionStart.bind(this), 50);
		} else {
			this.inst.setOpacity(0);
			this.inst.elem.style.display = this.inst.elem.displayStyle;
			this.inst.update(this.inst.getParentInnerBox());
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			this.inst.setOpacity(100);
			this.op = tactile.effects.Operations.show;
			this.start = (new Date).getTime();
			this.timer = setInterval(this.step.bind(this), 1000/this.fps);
		}
		tactile.logger.info(this.toString() + ", show: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
		return true;
	},
	hide: function(config) {
		config = config || {};
		var bs = tactile.Device.browser.style,
		op = tactile.effects.Operations;
		dim = config.expand || tactile.Enum.Dimension.width;
		if (this.op != op.none) {
			return false;
		}
		this.to = config.to || 0;
		if (dim == tactile.Enum.Dimension.width) {
			this.prop = tactile.effects.Properties.width;
			var nw = this.inst.validateNumber(this.inst.configWidth, this.inst.getParentInnerBox());
			this.from = nw ? nw : this.inst.getOuterBox().width;
		} else {
			this.prop = tactile.effects.Properties.height;
			var nh = this.inst.validateNumber(this.inst.configHeight, this.inst.getParentInnerBox());
			this.from = nh ? nh : this.inst.getOuterBox().height;
		}
		if (this.useTransition) {
			this.op = op.hide;
			this.inst.elem.style[this.prop] = this.from + tactile.Enum.Unit.px;
			window.setTimeout(
					function(){
						this.inst.elem.style[bs.transitionProperty] = this.prop;
						this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
						this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
						this.inst.elem.style[this.prop] = this.to + tactile.Enum.Unit.px;
						this.setEndEvent();
					}.bind(this),50);
		} else {
			this.op = op.hide;
			this.start = (new Date).getTime();
			this.timer = setInterval(this.step.bind(this), this.duration/this.fps);
		}
		tactile.logger.info(this.toString() + ", hide: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
		return true;
	},
	toString: function() {
		var method = "javascript, Frames per sec.: " + this.fps;
		if (this.useTransition) {
			method = "transition";
		}
		return "Object - tactile.effects.Expand: " + method;
	}
};
tactile.c.U.extend(tactile.effects.Expand, tactile.effects.Base);
tactile.effects.Fade = function(container, config) {
	this.init(container, config);
};
tactile.effects.Fade.prototype = {
		init: function(container, config){
			tactile.effects.Fade.superclass.init.apply(this, arguments);
			this.jsstep = function(delta) { this.inst.setOpacity(delta);};
			this.onend = new tactile.foundation.Observer();
			this.scroll = {x:0,y:0};
		},
		transitionEnd: function() {
			var bs = tactile.Device.browser.style, op = tactile.effects.Operations;
			if (this.op == op.hide) {
				if(this.inst.scroller){
				}
				setTimeout((function(){
					this.transitionEndHide();
					if (this.inst.afterhide) {
						this.inst.afterhide.fire({targetId:this.inst.id});			}
				}).bind(this), 50);
			} else if (this.op == op.show) {
				if (this.inst.aftershow) {
					this.inst.aftershow.fire({targetId:this.inst.id});
				}
			}
			this.inst.elem.style[bs.transitionProperty] = "";
			this.inst.elem.style[bs.transitionDuration] = "";
			this.inst.elem.style[bs.transitionTimingFunction] = "";
			this.onend.fire({operation: this.op});
			if (this.op == op.show) {
				if(tactile.Device.platform === "ios" && tactile.Device.version >= 5){
					window.setTimeout(function(){
						tactile.c.U.updateRenderTreeFix(this.inst.elem);
					}.bind(this),50);
				}
			}
			this.op = op.none;
		},
		transitionEndHide: function() {
			this.inst.elem.style.display = tactile.Enum.Display.none;
		},
		transitionStart: function() {
			var bs = tactile.Device.browser.style;
			this.inst.elem.style[bs.transitionProperty] = this.prop;
			this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
			this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
			this.inst.setOpacity(this.to);
			this.setEndEvent();
		},
		show: function(config) {
			var bs = tactile.Device.browser.style, op = tactile.effects.Operations;
			if (this.op != op.none) {
				return false;
			}
			config = config || {};
			this.from = config.from || 0;
			this.to = 100;
			this.prop = "opacity";
			if (this.useTransition) {
				this.inst.setOpacity(0);
				this.inst.elem.style.display = this.inst.elem.displayStyle;
				this.inst.update(this.inst.getParentInnerBox());
				this.op = op.show;
				setTimeout(this.transitionStart.bind(this), 50);
			} else {
				this.inst.setOpacity(0);
				this.inst.elem.style.display = this.inst.elem.displayStyle;
				this.inst.update(this.inst.getParentInnerBox());
				this.op = op.show;
				this.start = (new Date).getTime();
				this.timer = setInterval(this.step.bind(this), 1000/this.fps);
			}
			tactile.logger.info(this.toString() + ", show: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
			return true;
		},
		hide: function(config) {
			config = config || {};
			var bs = tactile.Device.browser.style,
			op = tactile.effects.Operations,
			dim = config.expand || tactile.Enum.Dimension.width;
			if (this.op != op.none) {
				return false;
			}
			if(this.inst.scroller && this.inst.scroller.scrolled){
			}
			this.to = config.to || 0;
			this.from = 100;
			this.prop = "opacity";
			if (this.useTransition) {
				this.op = op.hide;
				this.inst.elem.style[bs.transitionProperty] = this.prop;
				this.inst.elem.style[bs.transitionDuration] = this.duration + "ms";
				this.inst.elem.style[bs.transitionTimingFunction] = this.timing;
				this.inst.setOpacity(this.to);
				this.setEndEvent();
			} else {
				this.op = op.hide;
				this.start = (new Date).getTime();
				this.timer = setInterval(this.step.bind(this), this.duration/this.fps);
			}
			tactile.logger.info(this.toString() + ", hide: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
			return true;
		},
		toString: function() {
			var method = "javascript, ";
			if (this.useTransition) {
				method = "transition";
			}
			return "Object - tactile.effects.Fade: " + method;
		}
	};
tactile.c.U.extend(tactile.effects.Fade, tactile.effects.Base);
tactile.effects.Animator = function(element, step){
    var $this = {};
    if(typeof element == 'undefined' && typeof step == 'undefined'){
        return;
    }
    if(typeof element=="string") element = document.getElementById(element);
    $this.visibleCheckInterval = 90;
    $this.requestAnimationFrameSupport = true;
    $this.request;
    $this.runs = 0;
    $this.pause = false;
    $this.step = step;
    $this.el = element;
    $this.parents = [];
    window.requestAFrame = (function(){
        return  window.requestAnimationFrame   ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame
    })();
    if(!window.requestAFrame){
        window.requestAFrame = function(callback, element){
            return window.setTimeout(callback, 1000 / 60);
        };
        $this.requestAnimationFrameSupport = false;
    }
    window.cancelRequestAFrame = (function(){
        return window.cancelAnimationFrame            ||
            window.webkitCancelRequestAnimationFrame  ||
            window.mozCancelRequestAnimationFrame     ||
            window.oCancelRequestAnimationFrame       ||
            window.msCancelRequestAnimationFrame      ||
            clearTimeout
    })();
    $this.getElementParents = function(element){
        var elements = [];
        while(element.parentNode){
            element = element.parentNode;
            elements.push(element);
            if(element == parent) return elements;
        }
        return elements;
    }
    $this.getElementOffset = function(element){
        var _x = 0,
            _y = 0,
            prev_x = [],
            prev_y = [];
        while(element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)){
            if(    !((element.style.position == '' || element.style.position == 'static')
                && (prev_x.indexOf(element.offsetLeft)!=-1 || prev_y.indexOf(element.offsetTop)!=-1)) ){
                _y += element.offsetTop;
                _x += element.offsetLeft;
                prev_x.push(_x);
                prev_y.push(_y);
            }
            if(tactile.page.getComponent(element.id)){
                if(tactile.page.getComponent(element.id).scroller){
                    break;
                }
            }
            element = element.parentNode;
        }
        return {y: _y,x: _x};
    }
    $this.parents = $this.getElementParents($this.el);
    $this.offset = $this.getElementOffset($this.el);
    $this.isElementVisible = function(element, parents){
        for(var i=1;i < parents.length;i++){
            if(parents[i].tagName == 'DIV' && tactile.page.getComponent(parents[i].id)){
                if(!tactile.page.getComponent(parents[i].id).isVisible()){
                    return false;
                };
            }
        }
        return true;
    }
    $this.isElementInsideScrollArea = function(element, parents, offset){
        var scrollOffset = {},
            scrollViewport,
            element = tactile.page.getComponent(element.id);
        var dimensions = { height: element.elem.offsetHeight, width: element.elem.offsetWidth }
        for(var i=1;i < parents.length;i++){
            var targetElement = tactile.page.getComponent(parents[i].id);
            if(parents[i].tagName == 'DIV' && tactile.page.getComponent(parents[i].id)){
                if(typeof targetElement.scroller != 'undefined'){
                    scrollOffset = targetElement.scroller.offset;
                    scrollViewport = { height: targetElement.parent.elem.offsetHeight, width: targetElement.parent.elem.offsetWidth }
                    break;
                }
            }
        }
        var visible_y = !(scrollViewport.height < (offset.y - Math.abs(scrollOffset.y))) && !(Math.abs(scrollOffset.y) > (offset.y + dimensions.height)),
            visible_x = !(scrollViewport.width  < (offset.x - Math.abs(scrollOffset.x))) && !(Math.abs(scrollOffset.x) > (offset.x + dimensions.width));
        return visible_y && visible_x;
    }
    $this.stepOut = function(){
        $this.runs++;
        var m;
        if(!$this.pause){
            if(this.requestAnimationFrameSupport){
                m = $this.step();
                if($this.runs % $this.visibleCheckInterval === 0 && $this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)){
                    $this.pause = true;
                }
            }else{
                if($this.runs % $this.visibleCheckInterval === 0){
                    if($this.isElementVisible($this.el, $this.parents) && $this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)){
                        m = $this.step($this.runs);
                    }else{
                        $this.pause = true;
                    }
                }else{
                    m = $this.step($this.runs);
                }
            }
        }else{
            if($this.runs % $this.visibleCheckInterval === 0){
                if(this.requestAnimationFrameSupport){
                    if($this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)) $this.pause = false;
                }else{
                    if(    $this.isElementVisible($this.el, $this.parents)
                        && $this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)){
                        $this.pause = false;
                    }
                }
            }
        }
        if(!m && m != undefined && typeof(m) != 'string'){
            cancelRequestAFrame($this.request);
        } else {
            $this.request = requestAFrame($this.stepOut, $this.el);
        }
    };
    $this.stop = function(){
        cancelRequestAFrame($this.request);
    }
    $this.start = function(){
        if(this.requestAnimationFrameSupport){
            if($this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)) $this.stepOut();
        }else{
            if(   $this.isElementVisible($this.el, $this.parents)
               && $this.isElementInsideScrollArea($this.el, $this.parents, $this.offset)){
                $this.stepOut();
            }
        }
    }
    $this.start();
    return {
        element: $this.el,
        stop: $this.stop,
        start: $this.start
    }
};
tactile.foundation.Observer = function () {
    this.fns = [];
};
tactile.foundation.Observer.prototype = {
    subscribe : function(fn, scope) {
    	var _scope = scope || window;
        this.fns.push({"fn":fn,"scope":_scope});
    },
    unsubscribe : function(fn) {
        this.fns = this.fns.filter(
            function(el) {
                if ( el.fn !== fn ) {
                    return el;
                }
            }
        );
    },
    fire : function(o, thisObj) {
        this.fns.forEach(
            function(el) {
            	var scope = thisObj || el.scope;
                el.fn.call(scope, o);
            }
        );
    }
};
tactile.foundation.Scroller = function(view, config) {
	if (view instanceof tactile.View == false && view instanceof tactile.FlexView == false && view instanceof tactile.GridView == false) {
		throw new tactile.errors.TypeError("tactile.foundation.Scroller","tactile.View or tactile.FlexView or tactile.GridView",typeof view);
	}
	if (!view.parent){
		tactile.logger.warn("No parent specified for "+this.id+" "+this.toString());
	}
	if (!(view.parent instanceof tactile.foundation.UIContainer)) {
		throw new tactile.errors.TypeError("tactile.foundation.Scroller","tactile.foundation.UIContainer",typeof view.parent);
	}
	if (!config) {
		throw new tactile.errors.ArgumentUndefined("tactile.foundation.Scroller","config");
	}
	this.view = view;
	if(view instanceof tactile.FlexView || view instanceof tactile.GridView || view instanceof tactile.View){
        var elem = view.elem.childNodes[1] || view.elem.childNodes[0];
        elem.id = view.elem.id + Math.random().toString().replace('.','');
        this.elem = elem;
    } else {
        this.elem = view.elem;
    }
    this.elem.scrollContainer = view.elem;
	this.parent = view.parent;
	this.touchId = null;
	this.stopBubbling = false;
	this.onstart = new tactile.foundation.Observer();
	this.onscroll = new tactile.foundation.Observer();
	this.onend = new tactile.foundation.Observer();
	this.onboundary = new tactile.foundation.Observer();
	this.bounce = typeof config.bounce === "boolean" ? config.bounce : true;
	this.onbouncestart = new tactile.foundation.Observer();
	this.onbounceend = new tactile.foundation.Observer();
	this.getDirection = undefined;
	if (typeof config.scroll === "function") {
		this.getDirection = config.scroll;
	} else if (tactile.Enum.contains(tactile.Enum.Scroll, config.scroll)) {
		this.getDirection = function () {return tactile.Enum.getValue(tactile.Enum.Scroll, config.scroll);};
	} else {
		this.getDirection = function () {return tactile.Enum.Scroll.vertical;};
	}
	this.state = this.validDirection();
	var scrollbarconfig = {direction: config.scroll};
	if (config.scrollbar) {
		scrollbarconfig = config.scrollbar;
	}
	if (config.scrollbar && config.scrollbar.direction === undefined) {
		scrollbarconfig.direction = config.scroll;
	}
	if(scrollbarconfig.direction != tactile.Enum.Scroll.disabled){
		this.scrollbar = new tactile.foundation.ScrollBar(this, scrollbarconfig);
	}
	this.useTransform = tactile.Device.browser.csstransforms;
	this.useTransform3d = tactile.Device.browser.csstransforms3d && tactile.Device.platform === "ios";
	if(typeof nbdevice === "object" && typeof nbdevice.tactilescrollermethod === "string"){
		var method = nbdevice.tactilescrollermethod.toLowerCase();
		if(nbdevice.tactilescrollermethod){
			this.useTransform = (method === tactile.effects.Method.transform);
			this.useTransform3d = (method === tactile.effects.Method.transform3d);
		}
	}
	if (config.method && config.method == tactile.effects.Method.javascript) {
		this.useTransform = false;
		this.useTransform3d = false;
	}
	if (config.method && config.method == tactile.effects.Method.transform) {
		this.useTransform = true;
		this.useTransform3d = false;
	}
	if (config.method && config.method == tactile.effects.Method.transform3d) {
		this.useTransform = false;
		this.useTransform3d = true;
	}
	if(!this.useTransform && !this.useTransform3d){
		this.elem.style.position = "absolute";
	}
	var framesec = 25;
	if (tactile.Device.platform === "ios" && tactile.Device.version >= 3) {
		framesec = 100;
	}
	this.minVelocity = 0.1;
	this.topOffset = this.view.validateNumber(this.view.configTop, this.view.getParentInnerBox()) || 0;
	this.fps = config.fps || framesec;
	this.frameinterval = 1000 / this.fps;
	this.previousTime = 0;
	this.duration = 1000;
	this.transition = config.transition || tactile.effects.Transitions.outQuad;
	this.offset = {x:0, y:0};
	this.start = {x:0, y:0};
	this.from = {x:0, y:0};
	this.to = {x:0, y:0};
	this.timer = {x:null, y:null};
	this.startTime = {x:null, y:null};
	var self = this;
	this.elem.me = self;
	tactile.events.TouchEventManager.addListener(this.elem, "scrollstart", self.onScrollStart);
	tactile.events.TouchEventManager.addListener(this.elem, "scroll", self.onScrollMove);
	tactile.events.TouchEventManager.addListener(this.elem, "scrollend", self.onScrollEnd);
	this.view.onresize.subscribe(this.update, this);
	this.checkLimits();
	this.scrolling = false;
	this.scrolled = false;
	this.bouncing = false;
	this.boundary = false;
	var bs = tactile.Device.browser.style;
	if (this.useTransform3d) {
		this.elem.style[bs.transformProperty] = "translate3d(0px, 0px,0px)";
	} else {
		this.elem.style[bs.transformProperty] = "translate(0px, 0px)";
	}
};
tactile.foundation.Scroller.extend({
	validDirection: function() {
		var ret,dir;
		dir = "vertical";
		try {
			ret = this.getDirection();
		}catch(err){
			tactile.logger.error(this.id + ".getDirection() has thrown an exception: " + err);
		}
		if (tactile.Enum.contains(tactile.Enum.Scroll, ret)){
			dir = ret;
		}else{
			tactile.logger.error(this.id + ".getDirection(): TypeError ("+ret+" is not a valid direction)");
		}
		return dir;
	},
	scrollVertical: function() {
		if (this.validDirection() == tactile.Enum.Scroll.vertical || this.validDirection() == tactile.Enum.Scroll.both) {
			return true;
		} else {
			return false;
		}
	},
	scrollHorizontal: function() {
		if (this.validDirection() == tactile.Enum.Scroll.horizontal || this.validDirection() == tactile.Enum.Scroll.both) {
			return true;
		} else {
			return false;
		}
	},
	scrollBoth: function() {
		if (this.validDirection() == tactile.Enum.Scroll.both) {
			return true;
		} else {
			return false;
		}
	},
	checkLimits: function() {
		var totalWidth = this.elem.scrollContainer.scrollWidth;
		var totalHeight = this.elem.scrollContainer.scrollHeight;
		var factor = this.bounce ? 0 : 0.5;
		var box = this.parent.getInnerBox();
		var factorLess = this.bounce ? 1 : 0;
		var lessContentX = this.elem.scrollContainer.scrollWidth < box.width;
		var lessContentY = this.elem.scrollContainer.scrollHeight < box.height;
		if(tactile.Device.isDesktopBrowser){
			totalWidth += Math.abs(this.offset.x);
			totalHeight += Math.abs(this.offset.y);
		}
		var minLeft,maxLeft,maxTop,minTop;
		minLeft = lessContentX ? - this.elem.scrollContainer.offsetWidth*0.5*factorLess : - totalWidth + Math.round((0.5 + factor) * box.width);
		maxLeft = lessContentX ? box.width*0.5 *factorLess: Math.round((0.5 - factor)*box.width);
		maxTop = lessContentY ? box.height*0.5*factorLess : Math.round((0.5 - factor) * box.height);
		minTop = lessContentY ? -this.elem.scrollContainer.offsetHeight*0.5*factorLess : - totalHeight + box.height - Math.round((0.5 - factor) * box.height) - this.topOffset;
		this.limits = {
				minLeft : minLeft,
				maxLeft : maxLeft,
				minTop : minTop,
				maxTop : maxTop
			};
	},
	scrollProperty: {},
	scrollTimeMax: 300,
	onScrollStart: function(e) {
		var self = this.me;
		self.boundary = false;
		self.scrolled = false;
		if(e.browserEvent && e.browserEvent.touches){
			if(e.browserEvent.touches.length > 1 && self.touchId != null && e.browserEvent.touches[1].identifier != self.touchId){
				return;
			}
			self.touchId = e.browserEvent.touches[0].identifier;
		}
		self.checkLimits();
		self.start.x = e.pageX - self.offset.x;
		self.start.y = e.pageY - self.offset.y;
		self.previousTime = e.timeStamp;
		self.scrollProperty = {
	        old: (new Date()).getTime(),
	        now: (new Date()).getTime(),
	        startX: e.pageX,
	        startY: e.pageY
	    };
		if (self.timer.x) {
			clearInterval(self.timer.x);
			self.timer.x = null;
		}
		if (self.timer.y) {
			clearInterval(self.timer.y);
			self.timer.y = null;
		}
		if(!self.stopBubbling){
			self.onstart.fire({offsetX: self.offset.x, offsetY: self.offset.y, direction:self.validDirection()});
		}
	},
	onScrollMove: function(e) {
		function isInSandbox(child) {
			var node = child.parentNode;
			while (node !== null) {
				if (node.className && node.className.match('stopPropagation')) {
					return true;
				}
				node = node.parentNode;
			}
			return false;
		}
		if (e.target && isInSandbox(e.target)) { return; }
		var self = this.me,	bs = tactile.Device.browser.style, tx;
		if(self.view.state === "hiding" || self.view.state === "showing"){
			return;
		}
		if(e.browserEvent && e.browserEvent.touches){
			if(e.browserEvent.touches.length > 1 && self.touchId != null && e.browserEvent.touches[1].identifier != self.touchId){
				return;
			}
			self.touchId = e.browserEvent.touches[0].identifier;
		}
		if (self.scrollVertical() && (self.offset.y > self.limits.maxTop || self.offset.y < self.limits.minTop)){
			if(!self.boundary){
				self.onboundary.fire({offsetX:self.offset.x,offsetY:self.offset.y,direction:self.validDirection()});
				self.boundary = true;
			}
			return;
		}
		if (self.scrollHorizontal() && (self.offset.x > self.limits.maxLeft || self.offset.x < self.limits.minLeft)) {
			if(!self.boundary){
				self.onboundary.fire({offsetX:self.offset.x,offsetY:self.offset.y,direction:self.validDirection()});
				self.boundary = true;
			}
			return;
		}
		self.scrolling = true;
		var top = self.view.validateNumber(self.view.configTop, self.view.getParentInnerBox());
		self.view.elem.style.top = (top !== null) ? top+"px" : "";
		if(e.timeStamp - self.previousTime >= self.frameinterval){
			self.previousTime = e.timeStamp;
			var tmpx = self.scrollHorizontal() ? e.pageX - self.start.x : self.offset.x;
			var tmpy = self.scrollVertical() ? e.pageY - self.start.y : self.offset.y;
			if(!self.bounce && (tmpy > self.limits.maxTop || tmpy < self.limits.minTop)){
				self.stopBubbling = true;
				return;
			}else{
				self.stopBubbling = false;
			}
			self.offset.x = tmpx;
			self.offset.y = tmpy;
		if (self.useTransform || self.useTransform3d) {
			tx = 0 + tactile.Enum.Unit.px, ty = 0 + tactile.Enum.Unit.px;
			if (self.scrollHorizontal()) {
				tx = self.offset.x + tactile.Enum.Unit.px;
			}
			if (self.scrollVertical()) {
				ty = self.offset.y + tactile.Enum.Unit.px;
			}
			if (self.useTransform3d) {
				self.elem.style[bs.transformProperty] = "translate3d("+tx+", "+ty+",0px)";
			} else {
				self.elem.style[bs.transformProperty] = "translate("+tx+", "+ty+")";
			}
		} else {
			if (self.scrollHorizontal()) {
				self.elem.style.left = self.offset.x + tactile.Enum.Unit.px;
			}
			if (self.scrollVertical()) {
				self.elem.style.top = Math.round(self.offset.y) + tactile.Enum.Unit.px;
			}
		}
		self.scroll.x = self.offset.x;
		self.scroll.y = self.offset.y;
		self.scrolled = true;
		self.getCurrentPosition.call(this, e);
		self.onscroll.fire({offsetX: self.offset.x, offsetY: self.offset.y, direction:self.validDirection()});
		}
	},
	getCurrentPosition: function(e){
		  var self = this.me;
		    var now = (new Date()).getTime();
		    if(now - self.scrollProperty.now > self.scrollTimeMax){
		        self.scrollProperty.old = self.scrollProperty.now;
		        self.scrollProperty.now = now;
		        self.scrollProperty.startX = e.pageX;
		        self.scrollProperty.startY = e.pageY;
		    }
	},
	scrollBack: function() {
		var box = this.parent.getInnerBox();
		var from = {
			x : this.offset.x,
			y : this.offset.y
		};
		var to = {
			x : box.left,
			y : box.top
		};
		this.scroll(from, to);
	},
	reset: function() {
		var box = this.parent.getInnerBox(), left = 0, top = 0, bs = tactile.Device.browser.style;
		if (this.scrollBoth()) {
			left = box.left;
			top = box.top;
			this.offset.x = 0;
			this.offset.y = 0;
			if (!this.useTransform3d && !this.useTransform) {
				this.elem.style.left = left + tactile.Enum.Unit.px;
				this.elem.style.top = top + tactile.Enum.Unit.px;
			}
		} else if (this.scrollVertical()) {
			top = box.top;
			this.offset.y = 0;
			if (!this.useTransform3d && !this.useTransform) {
				this.elem.style.top = top + tactile.Enum.Unit.px;
			}
		} else {
			left = box.left;
			this.offset.x = 0;
			if (!this.useTransform3d && !this.useTransform) {
				this.elem.style.left = left + tactile.Enum.Unit.px;
			}
		}
		if (this.useTransform3d) {
			this.elem.style[bs.transformProperty] = "translate3d(0px,0px,0px)";
		} else if (this.useTransform) {
			this.elem.style[bs.transformProperty] = "translate(0px,0px)";
		}
	},
	onScrollEnd: function(e) {
		var self = this.me, velocity, now = (new Date()).getTime();
	    self.getCurrentPosition.call(this, e);
	    if(self.scrolled){
	    	velocity = {
	            x: (e.pageX - self.scrollProperty.startX)/ (now - self.scrollProperty.old),
	            y: (e.pageY - self.scrollProperty.startY)/ (now - self.scrollProperty.old)
	        };
			var box = self.parent.getInnerBox();
			if(self.bounce){
				if(self.scrollVertical()){
					if(self.offset.y > 0 || self.offset.y < (- self.elem.scrollHeight + box.height - self.topOffset)){
						self.bouncing = true;
					}
				}
				if(self.scrollHorizontal()){
					if(self.offset.x > 0 || self.offset.x < (- self.elem.scrollWidth + box.width)){
						self.bouncing = true;
					}
				}
				if(self.bouncing){
					self.onbouncestart.fire({offsetX:self.offset.x,offsetY:self.offset.y,direction:self.validDirection()});
				}
			}
			var duration = self.duration;
			if(self.scrollBoth()){
				duration = (Math.abs(velocity.y) > Math.abs(velocity.x)) ? Math.abs(velocity.y)*self.duration*0.7 : Math.abs(velocity.x)*self.duration*0.7;
			}else{
				duration =  self.scrollVertical() ? Math.abs(velocity.y)*self.duration*0.7 : Math.abs(velocity.x)*self.duration*0.7;
			}
			if (self.scrollVertical() && !self.scrollBoth()){
				if(Math.abs(velocity.y)<0.3){
					self.onComplete();
					return;
				}
			} else if(self.scrollHorizontal() && !self.scrollBoth()){
				if (Math.abs(velocity.x)<0.3){
					self.onComplete();
					return;
				}
			}
			var fadetoX = Math.round(velocity.x * duration);
			var fadetoY = Math.round(velocity.y * duration);
			var from = {
				x: self.offset.x,
				y: self.offset.y
			};
			var to = {
				x: from.x + fadetoX,
				y: from.y + fadetoY
			};
			if(!self.bounce){
				to.x = (to.x > self.maxLeft) ? self.maxLeft : to.x;
				to.x = (to.x < self.minLeft) ? self.minLeft : to.x;
				to.y = (to.y > self.maxTop) ? self.maxTop : to.y;
				to.y = (to.y < self.minTop) ? self.minTop : to.y;
			}
			self.scroll(from, to);
		}
	},
	onComplete: function() {
		var box = this.parent.getInnerBox();
		var bounce = false;
		if (this.scrollVertical()) {
			if (this.offset.y == 0) {
			}
			else if (this.offset.y > 0) {
					this.to.y = 0;
					this.scroll(this.offset, this.to);
					bounce = true;
			} else if (this.offset.y < - this.elem.scrollHeight + box.height - this.topOffset) {
					this.to.y = - this.elem.scrollHeight + box.height  - this.topOffset;
					this.scroll(this.offset, this.to);
					bounce = true;
			}
		}
		if (this.scrollHorizontal()) {
			if (this.offset.x == 0) {
			}
			else if (this.offset.x > 0) {
					this.to.x = 0;
					this.scroll(this.offset, this.to);
					bounce = true;
			} else if (this.offset.x < - this.elem.scrollWidth + box.width) {
					this.to.x = - this.elem.scrollWidth + box.width;
					this.scroll(this.offset, this.to);
					bounce = true;
			}
		}
		if(!bounce){
			this.scrolling = false;
			this.scroll.x = this.offset.x;
			this.scroll.y = this.offset.y;
			if(this.scrolled){
				if(!self.stopBubbling){
					this.onend.fire({offsetX: this.offset.x, offsetY: this.offset.y, direction:this.validDirection()});
				}
				if(this.bouncing){
					this.onbounceend.fire({offsetX:this.offset.x,offsetY:this.offset.y,direction:this.validDirection()});
					this.bouncing = false;
				}
			}
		}
	},
	scroll: function(from, to) {
		if (this.elem.timer !== undefined) {
			return;
		}
		this.checkLimits();
		var box = this.parent.getInnerBox();
		this.from = from;
		if (this.scrollVertical()) {
			if (to.y > 0) {
				this.to.y = 0;
			} else if (to.y <= this.limits.minTop) {
				this.to.y = - this.elem.scrollHeight + box.height - this.topOffset;
			} else {
				this.to.y = to.y;
			}
			this.startTime.y = (new Date()).getTime();
			this.timer.y = setInterval(this.step.bind(this),this.frameinterval);
		}
		if (this.scrollHorizontal()) {
			if (to.x > 0) {
				this.to.x = 0;
			} else if (to.x <= this.limits.minLeft) {
				this.to.x = -this.elem.scrollWidth + box.width;
			} else {
				this.to.x = to.x;
			}
			if(this.to.x != this.offset.x){
				this.startTime.x = (new Date()).getTime();
				this.timer.x = setInterval(this.step.bind(this), this.frameinterval);
			}
		}
	},
	move: function(to) {
		var box = this.parent.getBox();
		var target = {x:undefined,y:undefined};
		var from = {x:this.offset.x,y:this.offset.y};
		if(typeof to === "string"){
			var el = document.getElementById(to);
			if(el){
				var offset = {x:0,y:0};
				var curr = this;
				while(curr.parent){
					offset.x += curr.parent.getBox().left;
					offset.y += curr.parent.getBox().top;
					curr = curr.parent;
				}
				target = {x:-tactile.c.E.getOffsetLeft(el) + offset.x,y:-tactile.c.E.getOffsetTop(el) + offset.y};
			}else{
				tactile.logger.info(this.toString() + ", show: " + this.inst.id + ", from: " + this.from + ", to: " + this.to);
				tactile.logger.warn("No element with the id "+to+" found !");
			}
		}else{
			if(!isNaN(to.x) && !isNaN(to.y)){
				target = {x:this.offset.x + to.x, y:this.offset.y + to.y};
			}
		}
		if(!isNaN(target.x) && !isNaN(target.y)){
			this.scroll(from,target);
		}
	},
	step: function() {
		var time, Tpos, now, bs = tactile.Device.browser.style;
		if (this.scrollVertical() && this.timer.y != null) {
			time = (new Date()).getTime();
			if (time >= this.duration + this.startTime.y) {
				now = this.to.y;
				clearInterval(this.timer.y);
				this.timer.y = null;
				setTimeout(this.onComplete.bind(this), 20);
			} else {
				Tpos = (time - this.startTime.y) / (this.duration);
				now = Math.round(this.transition(Tpos) * (this.to.y - this.from.y) + this.from.y);
			}
			this.offset.y = now;
			this.scrolling = true;
			if (this.useTransform || this.useTransform3d) {
				if (this.useTransform3d){
					if(this.scrollBoth()){
						this.elem.style[bs.transformProperty] = "translate3d("+this.offset.x+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+",0px)";
					}else{
						this.elem.style[bs.transformProperty] = "translate3d(0"+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+",0px)";
					}
				} else {
					if(this.scrollBoth()){
						this.elem.style[bs.transformProperty] = "translate("+this.offset.x+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+")";
					}else{
						this.elem.style[bs.transformProperty] = "translate(0"+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+")";
					}
				}
			} else {
				this.elem.style.top = this.offset.y + tactile.Enum.Unit.px;
			}
		}
		if (this.scrollHorizontal() && this.timer.x != null) {
			time = (new Date()).getTime();
			if (time >= this.duration + this.startTime.x) {
				now = this.to.x;
				clearInterval(this.timer.x);
				this.timer.x = null;
				setTimeout(this.onComplete.bind(this), 20);
			} else {
				Tpos = (time - this.startTime.x) / (this.duration);
				now = Math.round(this.transition(Tpos) * (this.to.x - this.from.x) + this.from.x);
			}
			this.offset.x = now;
			if (this.useTransform || this.useTransform3d) {
				if (this.useTransform3d) {
					if(this.scrollBoth()){
						this.elem.style[bs.transformProperty] = "translate3d("+this.offset.x+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+",0px)";
					}else{
						this.elem.style[bs.transformProperty] = "translate3d("+this.offset.x+tactile.Enum.Unit.px+",0"+tactile.Enum.Unit.px+",0px)";
					}
				} else {
					if(this.scrollBoth()){
						this.elem.style[bs.transformProperty] = "translate("+this.offset.x+tactile.Enum.Unit.px+","+this.offset.y+tactile.Enum.Unit.px+")";
					}else{
						this.elem.style[bs.transformProperty] = "translate("+this.offset.x+tactile.Enum.Unit.px+",0"+tactile.Enum.Unit.px+")";
					}
				}
			} else {
				this.elem.style.left = now + tactile.Enum.Unit.px;
			}
		}
		this.onscroll.fire({offsetX: this.offset.x, offsetY: this.offset.y,direction:this.validDirection()});
	},
	update: function() {
		var newdir = this.validDirection(), bs = tactile.Device.browser.style;
		if (newdir != tactile.Enum.Scroll.disabled) {
			if (this.state != newdir) {
				if (this.useTransform || this.useTransform3d) {
					this.elem.style[bs.transformProperty] = "";
				} else {
					if(newdir === tactile.Enum.Scroll.vertical){
						this.elem.style.left = "0px";
					}else if(newdir === tactile.Enum.Scroll.horizontal){
						this.elem.style.top = "0px";
					}
				}
			}
			if(this.scrolled){
				if (this.scrollbar){
					tactile.c.E.setOpacity(this.scrollbar.vBar, 0);
					tactile.c.E.setOpacity(this.scrollbar.hBar, 0);
				}
				this.onComplete();
			}
		} else {
			if (this.useTransform || this.useTransform3d) {
				this.elem.style[bs.transformProperty] = "";
			} else {
				this.elem.style.left = "0px";
				this.elem.style.top = "0px";
			}
		}
		this.state = newdir;
	},
	onTouchMove: function(e) {
		e.browserEvent.preventDefault();
	    e.browserEvent.stopPropagation();
	},
	scrollBoundary: function(){
		var box = this.parent.getInnerBox();
	    if(this.scrollVertical())
	        if(this.offset.y > 0 || this.offset.y < (- this.elem.scrollHeight + box.height - this.topOffset)) this.bouncing = true;
	    if(this.scrollHorizontal())
	        if(this.offset.x > 0 || this.offset.x < (- this.elem.scrollWidth + box.width)) this.bouncing = true;
	    if(this.bouncing)
	        this.onbouncestart.fire({offsetX:this.offset.x,offsetY:this.offset.y, direction:this.validDirection()});
	    this.onComplete();
	},
	onTouchStart: function(e) {
		e.browserEvent.preventDefault();
	    e.browserEvent.stopPropagation();
	},
	toString: function() {
		var method = "javascript (style)";
		var method = "javascript (style)";
		if (this.useTransform3d) {
			method = "transform 3d";
		}else if (this.useTransform) {
			method = "transform (2d)";
		}
		return "Object - tactile.foundation.Scroller. Method: " + method + ", Frames per sec.: " + this.fps;
	}
});
tactile.foundation.ScrollBar = function(scroller, config) {
	if (!scroller instanceof tactile.foundation.Scroller) {
		throw tactile.errors.TypeError("tactile.foundation.ScrollBar","tactile.foundation.Scroller",typeof scroller);
	}
	this.scroller = scroller;
	this.scrollBarFixed = false;
	var el = document.createElement("div");
	el.style.padding = 0;
	el.style.position = tactile.Enum.Position.absolute;
	el.style.backgroundColor = config.color || "#cccccc";
	el.style.zIndex = (tactile.c.E.getZindex(this.scroller.elem) + 1).toString();
	el.style[tactile.Device.browser.style.borderRadius] = "2" + tactile.Enum.Unit.em;
	tactile.c.E.setOpacity(el, 0);
	this.vBar = el.cloneNode(true);
	this.hBar = el.cloneNode(true);
	if (typeof config.direction === "function") {
		this.getDirection = config.direction;
	} else if (tactile.Enum.contains(tactile.Enum.Scroll, config.direction)) {
		this.getDirection = function () {return tactile.Enum.getValue(tactile.Enum.Scroll, config.direction);};
	} else {
		this.getDirection = function () {return tactile.Enum.Scroll.vertical;};
	}
	this.weight = config.weight ? config.weight : 5;
	this.scroller.onstart.subscribe(this.onStart, this);
	this.scroller.onscroll.subscribe(this.onScroll, this);
	this.scroller.onend.subscribe(this.onEnd, this);
};
tactile.foundation.ScrollBar.extend({
	validDirection: function() {
		var ret,dir;
		dir = "vertical";
		ret = this.getDirection();
		if (tactile.Enum.contains(tactile.Enum.Scroll, ret)){
			dir = ret;
		}
		return dir;
	},
	fixScrollbar: function() {
		this.scroller.parent.elem.appendChild(this.vBar);
		this.scroller.parent.elem.appendChild(this.hBar);
		this.scrollBarFixed = true;
	},
	scrollVertical: function() {
		if (this.validDirection() == tactile.Enum.Scroll.vertical || this.validDirection() == tactile.Enum.Scroll.both) {
			return true;
		} else {
			return false;
		}
	},
	scrollHorizontal: function() {
		if (this.validDirection() == tactile.Enum.Scroll.horizontal || this.validDirection() == tactile.Enum.Scroll.both) {
			return true;
		} else {
			return false;
		}
	},
	getZindex: function(){
		var tmp,zindex = 0;
		var elem = this.scroller.elem;
		tmp = tactile.c.E.getCurrStyle(elem,"z-index");
		if(Number(tmp)){
			zindex = Number(tmp);
		}
		return zindex+1;
	},
	onStart: function(e) {
		if(!this.scrollBarFixed){
			this.fixScrollbar();
		}
		var box = this.scroller.parent.getBox();
		this.vertRatio = box.height * 100 / this.scroller.elem.scrollHeight;
		this.horizRatio = box.width * 100 / this.scroller.elem.scrollWidth;
		var indHeight = box.height * this.vertRatio / 100;
		var indWidth = box.width * this.horizRatio / 100;
		if (this.scrollVertical()) {
			this.vBar.style.width = this.weight + tactile.Enum.Unit.px;
			this.vBar.style.height = indHeight + tactile.Enum.Unit.px;
			this.vBar.style.top = (this.vBar.offsetTop + box.top) + tactile.Enum.Unit.px;
			this.vBar.style.left = (this.scroller.elem.offsetLeft + this.scroller.elem.scrollWidth - this.vBar.offsetWidth-2) + tactile.Enum.Unit.px;
			if(this.vertRatio < 100){
				tactile.c.E.setOpacity(this.vBar,60);
			}else{
				tactile.c.E.setOpacity(this.vBar,0);
			}
		}
		if (this.scrollHorizontal()) {
			this.hBar.style.left = (this.hBar.offsetLeft + box.left) + tactile.Enum.Unit.px;
			this.hBar.style.top = (box.height- 5) + tactile.Enum.Unit.px;
			this.hBar.style.width = indWidth + tactile.Enum.Unit.px;
			this.hBar.style.height = this.weight + tactile.Enum.Unit.px;
			if(this.horizRatio < 100){
				tactile.c.E.setOpacity(this.hBar,60);
			}else{
				tactile.c.E.setOpacity(this.hBar,0);
			}
		}
	},
	onScroll: function(e) {
		var horzOffset = Math.round(e.offsetX * this.horizRatio / 100);
		var vertOffset = Math.round(e.offsetY * this.vertRatio / 100);
		var box = this.scroller.parent.getBox();
		if (this.scrollVertical()) {
			if(this.scroller.offset.y >= 0){
				this.vBar.style.top = 0 + tactile.Enum.Unit.px;
			}else if(this.scroller.offset.y <= - this.scroller.elem.scrollHeight + box.height){
				this.vBar.style.top = (box.height - this.vBar.offsetHeight)+ tactile.Enum.Unit.px;
			}else{
				this.vBar.style.top = - vertOffset + tactile.Enum.Unit.px;
			}
		}
		if (this.scrollHorizontal()) {
			if (this.scroller.offset.x >= 0) {
				this.hBar.style.left = 0 + tactile.Enum.Unit.px;
			}else if(this.scroller.offset.x <= -this.scroller.elem.scrollWidth + box.width){
				this.hBar.style.left = (box.width - this.hBar.offsetWidth)+ tactile.Enum.Unit.px;
			}else{
				this.hBar.style.left = - horzOffset + tactile.Enum.Unit.px;
			}
		}
	},
	onEnd: function(e) {
		if (this.scrollVertical()) {
			tactile.c.E.setOpacity(this.vBar,0);
		}
		if (this.scrollHorizontal()) {
			tactile.c.E.setOpacity(this.hBar,0);
		}
	}
});
tactile.foundation.Layouter = function (config) {
	this.config = config || {};
	this.debug = false;
	this.device = undefined;
	this.orientation = undefined;
};
tactile.foundation.Layouter.extend({
	getDevice: function () {
		this.device = "tablet";
		if (tactile.Device.isSmartphone) {
			this.device = "smartphone";
		};
	},
	getOrientation: function () {
		this.orientation = "portrait";
		if (tactile.Device.screen.isLandscape())
			this.orientation = "landscape";
	},
	getValue: function (attribute, parent) {
		this.getDevice();
		this.getOrientation();
		var val = this.config[this.device][this.orientation][attribute] || 0;
		if (typeof(val) == "function") {
			val = val(parent);
		} else if (typeof(val) == "string") {
			if (val.indexOf("%") < 0 || val.indexOf("%") != (val.length - 1)) {
				val = eval("(function (parent) { return " + val + "})(parent);");
			} else {
			}
		} else if (typeof(val) == "boolean") {
			return val;
		} else if (typeof(val) != "number") {
			throw new tactile.errors.ArgumentUndefined("Layouter.config["+this.device+"]["+this.orientation+"]["+attribute+"] = "+val);
		}
		tactile.logger.info( "Layouter.config["+this.device+"]["+this.orientation+"]["+attribute+"] = " + val );
		return val;
	},
	getTop: function (parent) {
		var val = this.getValue("top", parent);
		tactile.logger.info( "Layouter.getTop() = "+val);
		return val;
	},
	getLeft: function (parent) {
		var val = this.getValue("left", parent);
		tactile.logger.info( "Layouter.Left() = "+val);
		return val;
	},
	getWidth: function (parent) {
		this.parent = parent;
		var val = this.getValue("width", parent);
		tactile.logger.info( "Layouter.getWidth() = "+val);
		return val;
	},
	getHeight: function (parent) {
		var val = this.getValue("height", parent);
		tactile.logger.info( "Layouter.getHeight() = "+val);
		return val;
	},
	getVisibility: function (parent) {
		var val = this.getValue("visibility", parent);
		if (val != true)
			val = false;
		tactile.logger.info( "Layouter.getVisibility() = "+val);
		return val;
	},
	getItemwidth: function (parent) {
		var val = this.getValue("itemwidth", parent);
		tactile.logger.info( "Layouter.getItemheight() = "+val);
		return val;
	},
	getItemheight: function (parent) {
		var val = this.getValue("itemheight", parent);
		tactile.logger.info( "Layouter.getItemwidth() = "+val);
		return val;
	}
});
tactile.foundation.ImageScaler = (function() {
	var Imagescaler = function() {
		this.loadingmask_latency = 100;
		this.latency = 50;
		this.onimageload = new tactile.foundation.Observer();
		this.oninit = new tactile.foundation.Observer();
		this.onready = new tactile.foundation.Observer();
		this.imagestoload     	      = undefined;
		this.portraitimagestoload     = undefined;
		this.landscapeimagestoload    = undefined;
		this.startLandscape      = false;
		this.startPortrait 		 = false;
		this.onreadyfired		 = false;
		this.imagestoscale       = [];
		this.imagestoscalelazy   = [];
		this.onreadytimer        = undefined;
		return this;
	};
	Imagescaler.extend({
		getLoadingmaskCls: function (view) {
			if (!view)
				return;
			if (view.loadingmaskCls == undefined) {
				view.loadingmaskCls = false;
				var match = view.elem.className.match(/(loadingmask_\w+|loadingmask)/);
				if (match && match[0]) {
					view.loadingmaskCls = match[0];
				}
			}
			return view.loadingmaskCls;
		},
		hideLoadingmask: function () {
			this.style.visibility = "visible";
		    if (!this.imagescaler)
		    	return;
			window.setTimeout((function () {
				var cls = this.imagescaler.getLoadingmaskCls(this.view);
				if (cls) {
					if (tactile.c != undefined) {
						tactile.c.E.removeClass(this.view.elem, cls);
					}
					else if (tactile.c != undefined) {
						tactile.c.E.removeClass(this.view.elem, cls);
					}
				}
				 this.imagescaler.loadingmask_latency = 0;
			}).bind(this), this.imagescaler.loadingmask_latency);
		},
		showLoadingmask: function () {
			this.style.visibility = "hidden";
			var cls = this.imagescaler.getLoadingmaskCls(this.view);
			if (cls) {
				if (tactile.c != undefined) {
					tactile.c.E.addClass(this.view.elem, cls);
				}
				else if (tactile.c != undefined) {
					tactile.c.E.addClass(this.view.elem, cls);
				}
			}
		},
		extractParams: function (delimiter, url, params) {
			tokes = url.split(delimiter);
			for (var i=1; i<tokes.length; i++) {
				nv = tokes[i].split('=');
				params[nv[0].toLowerCase()] = nv[1];
			}
			return params;
		},
		getImgUrl: function (src) {
			return decodeURIComponent(src.substr(src.indexOf("url") + 4));
		},
		getParams: function (src, _params) {
			var params = _params || {};
			var url = this.getImgUrl(src);
			if (url && url.match(/;/)) {
				params = this.extractParams(';', url, params);
			}
			return params;
		},
		cleansrc: function (src) {
			var url = this.getImgUrl(src);
			if (url) {
				matches = url.match(/&|;/);
				if (matches) {
					var delimiter = matches[0];
					src = src.substr(0, src.indexOf("url")) + "url=" + url.substr(0, url.indexOf(delimiter));
				}
			}
			return src;
		},
		getOffsets: function (src, e) {
			var eventdata = e || { height: 0, width: 0 };
			var orientation = this.getOrientation();
			var w, h;
			var params = this.getParams(src, { width: undefined, height: undefined });
			w = eventdata.width;
			h = eventdata.height;
			if (h > 0 && params.width == "auto") {
				w = 10000;
			}
			if (w > 0 && params.height == "auto") {
				h = 10000;
			}
			if (parseInt(params.width) == params.width) {
				w = params.width;
			}
			if (parseInt(params.height) == params.height) {
				h = params.height;
			}
			var out = {
				params : params,
				eventdata: eventdata,
				orientation : orientation,
				w : w,
				h : h
			};
			return out;
		},
		updateStyleCenter: function () {
			with (this.style) {
				position = "absolute";
				left = (((this.offsetWidth * 0.5) - (this.offsets.eventdata.width * 0.5)) * -1) + "px";
				top = (((this.offsetHeight * 0.5) - (this.offsets.eventdata.height * 0.5)) * -1) + "px";
			}
		},
		updateViewPosition: function () {
			var position = tactile.c.E.getCurrStyle(this.elem, "position");
			if (position == "static")
				this.elem.style.position = "relative";
		},
		updateStyle: function (w, h, offsets) {
			var need = 0;
			if (w > 0)
				this.style.width  = w + tactile.Enum.Unit.px;
			if (h > 0)
				this.style.height = h + tactile.Enum.Unit.px;
			if ((offsets.params.cliptop != undefined && offsets.params.cliptop == "true")
				|| (offsets.params.clipleft != undefined && offsets.params.clipleft == "true")
				|| (offsets.params.clipbottom != undefined && offsets.params.clipbottom == "true")
				|| (offsets.params.clipright != undefined && offsets.params.clipright == "true")
				|| (offsets.params.clipcenter != undefined && offsets.params.clipcenter == "true")) {
				if (this.parentNode.tagName == "A") {
					this.parentNode.parentNode.parentNode.style.overflow = "hidden";
					this.parentNode.parentNode.parentNode.style.position = "relative";
				} else {
					this.parentNode.parentNode.style.overflow = "hidden";
					this.parentNode.parentNode.style.position = "relative";
				}
			}
			if ((offsets.params.cliptop != undefined && offsets.params.cliptop == "true")
				|| (offsets.params.alignbottom != undefined	&& offsets.params.alignbottom == "true")) {
				with (this.style) {
					position = "absolute";
					bottom = "0px";
				}
				need = -1;
				this.imagescaler.updateViewPosition.call(this.view);
			}
			if ((offsets.params.clipbottom != undefined && offsets.params.clipbottom == "true")
				|| (offsets.params.aligntop != undefined && offsets.params.aligntop == "true")) {
				with (this.style) {
					position = "absolute";
					top = "0px";
				}
				need = -1;
				this.imagescaler.updateViewPosition.call(this.view);
			}
			if ((offsets.params.clipleft != undefined && offsets.params.clipleft == "true")
				|| (offsets.params.alignright != undefined && offsets.params.alignright == "true")) {
				with (this.style) {
					position = "absolute";
					right = "0px";
				}
				need++;
				this.imagescaler.updateViewPosition.call(this.view);
			}
			if ((offsets.params.clipright != undefined && offsets.params.clipright == "true")
				|| (offsets.params.alignleft != undefined && offsets.params.alignleft == "true")) {
				with (this.style) {
					position = "absolute";
					left = "0px";
				}
				need++;
				this.imagescaler.updateViewPosition.call(this.view);
			}
			if ((offsets.params.clipcenter != undefined && offsets.params.clipcenter == "true")
				|| (offsets.params.center != undefined && offsets.params.center == "true")
				|| (offsets.params.aligncenter != undefined && offsets.params.aligncenter == "true")) {
				this.offsets = offsets;
				this.imagescaler.updateViewPosition.call(this.view);
				if (this.complete) {
					this.imagescaler.updateStyleCenter.call(this);
				}
				else {
					this.addEventListener("load", function () {
						this.imagescaler.updateStyleCenter.call(this);
						this.removeEventListener("load", arguments.callee);
					});
				}
			} else if (need < 0)
				this.style.left = "0px";
		},
		updateHandler: function () {
			var par = this;
			var isc = par.imagescaler;
			var offsets = isc.getOffsets(this.targetsrc, par.view.getInnerBox());
			if (offsets.w < 1 && offsets.h < 1)
				return;
			var imb = {};
			if (tactile.Device.screen.isLandscape()) {
				imb.width   = this.landscape.naturalWidth;
				imb.height  = this.landscape.naturalHeight;
			} else {
				imb.width   = this.portrait.naturalWidth;
				imb.height  = this.portrait.naturalHeight;
			}
			var ima1 = {};
			ima1.width  = (offsets.w >= imb.width ? imb.width : offsets.w);
			ima1.height = imb.height * (ima1.width/imb.width);
			var ima2 = {};
			ima2.height = (offsets.h >= imb.height ? imb.height : offsets.h);
			ima2.width  = imb.width * (ima2.height/imb.height);
			if (offsets.h >= ima1.height) {
				ima = {
					width:  ima1.width,
					height: ima1.height
				};
			} else {
				ima = {
					width:  ima2.width,
					height: ima2.height
				};
			}
			isc.updateStyle.apply(par, [ima.width, ima.height, offsets]);
		},
		getOrientation: function () {
			if (tactile.Device.screen.isLandscape())
				return "landscape";
			if (tactile.Device.screen.isPortrait())
				return "portrait";
		},
		baseName: function (url) {
		    var fileName = url.substring(url.lastIndexOf('/') + 1);
		    var dot = fileName.lastIndexOf('.');
		    return dot == -1 ? fileName : fileName.substring(0, dot);
		},
		run: function (node) {
			var target = node || document;
			var params = {};
			var images = target.querySelectorAll("img[altsrc]");
			for (var i = 0; i < images.length; i++) {
				images[i].targetsrc = images[i].getAttribute("altsrc");
				images[i].removeAttribute("altsrc");
				if (images[i].getAttribute("alt") != "")
					images[i].screenname = images[i].getAttribute("alt");
				else
					images[i].screenname = this.baseName(this.getImgUrl(this.cleansrc(images[i].targetsrc)));
				var pnode = images[i].parentNode;
				var tagName = pnode.tagName.toLowerCase();
				while (
					pnode
					&& !tactile.page.isComponent(pnode.id)
					&& !(tactile.page.getComponent(pnode.id) instanceof tactile.View)
				) {
					pnode = pnode.parentNode;
				}
				var pNodeComp = undefined;
				if (pnode) {
					pNodeComp = tactile.page.getComponent(pnode.id);
				}
				if (pNodeComp && (typeof(pNodeComp.configVisible) !== "function"
					|| pNodeComp.configVisible() == true))	{
					params = this.getParams(images[i].targetsrc);
					if (params.lazyload && params.lazyload == "true") {
						images[i].lazyload = true;
						this.imagestoscalelazy.push({
							node: pNodeComp,
							img: images[i]
						});
					} else {
						images[i].lazyload = false;
						this.imagestoscale.push({
							node: pNodeComp,
							img: images[i]
						});
					}
				}
				if (!pNodeComp) {
					this.onimageload.fire({id: null, index: 0, length: 0, img: null});
					this.hideLoadingmask.call(images[i]);
					images[i].src = this.cleansrc(images[i].targetsrc);
				}
			};
			this.setup();
			return target;
		},
		setup: function () {
			this.portraitimagestoload  = 0;
			this.landscapeimagestoload = 0;
			var registerImages = [];
			for (var i=0; i<this.imagestoscale.length; i++) {
				if (!this.imagestoscale[i].img.imagescaler) {
					registerImages.push(this.imagestoscale[i]);
					this.portraitimagestoload++;
					this.landscapeimagestoload++;
				}
				else {
					if (!this.imagestoscale[i].img.portrait.complete || this.imagestoscale[i].img.portrait.src == "")
						this.portraitimagestoload++;
					if (!this.imagestoscale[i].img.landscape.complete || this.imagestoscale[i].img.landscape.src == "")
						this.landscapeimagestoload++;
				}
			}
			this.imagestoload = this.portraitimagestoload + this.landscapeimagestoload;
			if ((this.getOrientation() == "portrait" && this.portraitimagestoload > 0)
				|| (this.getOrientation() == "landscape" && this.landscapeimagestoload > 0)) {
				this.oninit.fire({
					orientation: this.getOrientation(),
					index: (2 * this.imagestoscale.length) - this.imagestoload,
					length: (2 * this.imagestoscale.length)
				});
			}
			if (registerImages.length > 0) {
				for (var i=0; i<registerImages.length; i++) {
					registerImages[i].img.src = "";
					this.init(
						registerImages[i].node,
						registerImages[i].img
					);
				};
			}
			for (var i=0; i<this.imagestoscalelazy.length; i++) {
				this.init(
					this.imagestoscalelazy[i].node,
					this.imagestoscalelazy[i].img
				);
			};
		},
		init: function (view, image) {
			image.view = view;
			image.imagescaler = this;
			image.orientation = undefined;
			var onErrorHandler = function () {
				var par = this.parentimage;
				var isc = par.imagescaler;
				par.hasError = true;
				isc.finalize.call(this);
			};
			var onLoadHandler = function () {
				var par = this.parentimage;
				var isc = par.imagescaler;
				par.hasError = false;
				if (!this.complete)
						return;
				this.loaded = true;
				par.removeAttribute("width");
				par.removeAttribute("height");
				if (isc.getOrientation() == this.orientation) {
					par.src = this.src;
					isc.updateHandler.call(par);
				}
				isc.hideLoadingmask.call(par);
				isc.finalize.call(this);
			};
			image.landscape = new Image;
			image.landscape.orientation = "landscape";
			image.landscape.targetsrc = image.targetsrc;
			image.landscape.parentimage = image;
			image.landscape.loaded = false;
			image.landscape.addEventListener("error", onErrorHandler);
			image.landscape.addEventListener("load", onLoadHandler);
			image.portrait = new Image;
			image.portrait.orientation = "portrait";
			image.portrait.targetsrc = image.targetsrc;
			image.portrait.parentimage = image;
			image.portrait.loaded = false;
			image.portrait.addEventListener("error", onErrorHandler);
			image.portrait.addEventListener("load", onLoadHandler);
			view.image = image;
			view.imagescaler = this;
			view.onstatechange.subscribe(this.initHandler, view);
			view.onresize.subscribe(this.initHandler, view);
			view.onresize.subscribe(this.updateHandler.bind(image));
		},
		initHandler: function (e) {
			var isc = this.imagescaler;
			if (isc.onreadyfired == true
				&& (
					(isc.getOrientation() == "portrait" && isc.portraitimagestoload > 0)
					|| (isc.getOrientation() == "landscape" && isc.landscapeimagestoload > 0)
				)) {
				isc.onreadyfired = false;
				isc.oninit.fire({
					orientation: isc.getOrientation(),
					index: (2 * isc.imagestoscale.length) - isc.imagestoload,
					length: (2 * isc.imagestoscale.length)
				});
			}
			switch (this.state) {
				case "hidden" :
				case "hiding" :
					return;
				break;
				case "showing" :
					this.onstatechange.subscribe((function () {
						window.setTimeout((function () {
							this.imagescaler.initHandler.call(this);
						}).bind(this), this.imagescaler.latency);
						this.onstatechange.unsubscribe(arguments.callee);
					}).bind(this));
					return;
				break;
			}
			var loadHandler = function () {
				if (this.timer)
					window.clearTimeout(this.timer);
				var par = this.parentimage;
				var isc = par.imagescaler;
				if (this.offsets.orientation == "landscape")
					isc.startedLandscape = true;
				if (this.offsets.orientation == "portrait")
					isc.startedPortrait = true;
				var dpr = 1;
				if(window.devicePixelRatio !== undefined)
					dpr = window.devicePixelRatio;
				this.newsrc = isc.cleansrc(par.targetsrc)
					.replace(/height=\d+/gi, 'height=' + (Math.round(this.offsets.h * dpr)))
					.replace(/width=\d+/gi,  'width='  + (Math.round(this.offsets.w * dpr)));
				this.timer = window.setTimeout((function () {
					if (this.src != this.newsrc) {
						isc.showLoadingmask.call(par);
						this.src = this.newsrc;
					}
				}).bind(this), isc.latency);
			};
			var offsets = this.imagescaler.getOffsets(this.image.targetsrc, this.getInnerBox());
			if (offsets.orientation == "landscape"
				&& this.image.landscape.loaded)
			{
				this.image.src = this.image.landscape.src;
			    isc.updateHandler.call(this.image);
			}
			else if (offsets.orientation == "portrait"
				&& this.image.portrait.loaded)
			{
				this.image.src = this.image.portrait.src;
				isc.updateHandler.call(this.image);
			}
			else if (offsets.w > 0
				&& offsets.h > 0
				&& offsets.orientation == "landscape"
				&& !this.image.landscape.loaded)
			{
				this.image.landscape.offsets = offsets;
				loadHandler.call(this.image.landscape);
			}
			else if (offsets.w > 0
				&& offsets.h > 0
				&& offsets.orientation == "portrait"
				&& !this.image.portrait.loaded)
			{
				this.image.portrait.offsets = offsets;
				loadHandler.call(this.image.portrait);
			}
		},
		onreadyHandler: function () {
			if (this.onreadytimer)
				window.clearTimeout(this.onreadytimer);
			var timerCallback = function () {
				this.onreadyfired = true;
				this.onready.fire({
					orientation: this.getOrientation(),
					index: (2 * this.imagestoscale.length) - this.imagestoload,
					length: (2 * this.imagestoscale.length)
				});
			};
			this.onreadytimer = window.setTimeout(timerCallback.bind(this), this.latency)
		},
		finalize: function () {
			var par = this.parentimage;
			var isc = par.imagescaler;
			var rest;
			if (!par.lazyload) {
				if (this.orientation == "landscape")
					isc.landscapeimagestoload--;
				else
					isc.portraitimagestoload--;
				isc.imagestoload--;
				isc.onimageload.fire({
					id: par.id,
					index: (2 * isc.imagestoscale.length) - isc.imagestoload,
					length: (2 * isc.imagestoscale.length),
					orientation: this.orientation,
					img: par
				});
				if (!isc.onreadyfired) {
					if (isc.startedPortrait
						&& isc.startedLandscape
						&& isc.imagestoload == 0)
						isc.onreadyHandler.call(isc);
					else if (
						((isc.startedPortrait && !isc.startedLandscape) || (isc.startedLandscape && !isc.startedPortrait))
						&& (isc.imagestoscale.length - (isc.imagestoload)) == 0)
						isc.onreadyHandler.call(isc);
				}
			}
			return true;
		}
	});
	return new Imagescaler();
})();
tactile.foundation.Element = function() {
	this.elem = undefined;
	this.init = function(elem) {
		if (typeof elem === "string") {
			this.elem = document.getElementById(elem);
		} else {
			this.elem = elem;
		}
		if (!this.elem) {
			throw new tactile.errors.ArgumentUndefined("tactile.foundation.Element.init","elem");
		}
		this.elem.displayStyle = this.elem.style.display;
	};
};
tactile.foundation.Element.extend({
	hide: function(config) {
		this.elem.style.display = tactile.Enum.Display.none;
	},
	show: function() {
		this.elem.style.display = this.elem.displayStyle;
	},
	toggle: function() {
		if (this.elem.style.display == tactile.Enum.Display.none) {
			this.show();
		} else {
			this.hide();
		}
	},
	setOpacity: function(opacity) {
		tactile.c.E.setOpacity(this.elem,opacity);
	},
	getOpacity: function() {
		return tactile.c.E.getOpacity(this.elem);
	},
	addClass: function(selector) {
		tactile.c.E.addClass(this.elem, selector);
	},
	removeClass: function(selector) {
		tactile.c.E.removeClass(this.elem, selector);
	},
	replaceClass: function(selector1, selector2) {
		tactile.c.E.replaceClass(this.elem, selector1, selector2);
	},
	toggleClass: function(selector) {
		tactile.c.E.toggleClass(this.elem, selector);
	},
	swapClass: function(selector1, selector2) {
		tactile.c.E.swapClass(this.elem, selector1, selector2);
	},
	isVisible: function() {
		return (this.elem.style.display != tactile.Enum.Display.none) && (tactile.c.E.getCurrStyle(this.elem,"display") != tactile.Enum.Display.none);
	}
});
tactile.foundation.UIElement = function(elem) {
	tactile.foundation.UIElement.prototype.init.call(this, elem);
};
tactile.foundation.UIElement.prototype = new tactile.foundation.Element();
tactile.foundation.UIElement.prototype.toString = function() {
	return "Object - tactile.foundation.UIElement";
};
tactile.foundation.UIComponent = function() {
	this.effect = undefined;
	this.aftershow = undefined;
	this.beforeshow = undefined;
	this.afterhide = undefined;
	this.beforehide = undefined;
	this.init = function(elem) {
		tactile.foundation.UIComponent.prototype.init.call(this, elem);
		if (this.elem.id) {
			this.id = this.elem.id;
		} else {
			this.id = this.elem.id = tactile.foundation.UIComponent.getNewId();
		}
		tactile.foundation.UIComponent.components[this.id] = this;
	};
};
tactile.foundation.UIComponent.prototype = new tactile.foundation.Element();
tactile.foundation.UIComponent.idSeed = 0;
tactile.foundation.UIComponent.getNewId = function() {
	return "UID_" + (tactile.foundation.UIComponent.idSeed += 1);
};
tactile.foundation.UIComponent.components = {};
tactile.foundation.UIComponent.extend({
	hide: function(config) {
		if (this.effect && this.effect.hide) {
			if (this.effect.op == tactile.effects.Operations.none) {
				if (this.beforehide) {
					this.beforehide.fire({targetId:this.id});
				}
				this.effect.hide(config);
			}
		} else {
			if (this.beforehide) {
				this.beforehide.fire({targetId:this.id});
			}
			this.elem.style.display = tactile.Enum.Display.none;
			if (this.afterhide) {
				this.afterhide.fire({targetId:this.id});
			}
		}
		if(this.scroller){
	        this.scroller.scrollBoundary();
	    }
	},
	show: function(config) {
		if(this.effect && this.effect.show){
			if (this.effect.op == tactile.effects.Operations.none) {
				if (this.beforeshow) {
					this.beforeshow.fire({targetId:this.id});
				}
				this.effect.show(config);
			}
		} else {
			if (this.beforeshow) {
				this.beforeshow.fire({targetId:this.id});
			}
			this.elem.style.display = this.elem.displayStyle;
			if(tactile.Device.platform === "ios" && tactile.Device.version >= 5){
				tactile.c.U.updateRenderTreeFix();
			}
			if (this.aftershow) {
				this.aftershow.fire({targetId:this.id});
			}
		}
	},
	toggle: function(config) {
			if (this.elem.style.display == tactile.Enum.Display.none) {
				this.show(config);
			} else {
				this.hide(config);
			}
	},
	append: function(component) {
		if (component instanceof tactile.foundation.UIComponent) {
			if(this instanceof tactile.FlexView || this instanceof tactile.GridView || this instanceof tactile.View){
	            var elem = this.elem.childNodes[1] || this.elem.childNodes[0];
				elem.appendChild(component.elem);
	        } else {
	            this.elem.appendChild(component.elem);
	        }
		} else {
			tactile.logger.error(this.id + " Type mismatch: tactile.foundation.UIComponent.append expects argument of type tactile.foundation.UIComponent!");
		}
	},
	prepend: function(component) {
		if (component instanceof tactile.foundation.UIComponent) {
			if(this instanceof tactile.FlexView || this instanceof tactile.GridView || this instanceof tactile.View){
				var elem = this.elem.childNodes[1] || this.elem.childNodes[0];
				elem.insertBefore(component.elem, elem.childNodes[0]);
	        } else {
	            this.elem.insertBefore(component.elem, this.elem.childNodes[0]);
	        }
		} else {
			tactile.logger.error(this.id + " Type mismatch: tactile.foundation.UIComponent.prepend expects argument of type tactile.foundation.UIComponent!");
		}
	},
	getChildren: function(array) {
		if(!array){
			var array = new Array();
		}
		if(this.elem){
			if(this.elem.hasChildNodes()){
				var children;
				if(this instanceof tactile.FlexView || this instanceof tactile.GridView || this instanceof tactile.View){
					var elem = this.elem.childNodes[1] || this.elem.childNodes[0];
					children = elem.childNodes;
		        } else {
		        	children = this.elem.childNodes;
		        }
				var ChildComp = null;
				for(var i=0;i<children.length;i++){
					if(children[i].id && tactile.page.isComponent(children[i].id)){
						ChildComp = tactile.page.getComponent(children[i].id);
						array.push(ChildComp);
						tactile.foundation.UIComponent.prototype.getChildren.call(ChildComp,array);
					}
				}
			}
		}
		return array;
	},
	remove: function(component) {
		function removeChildren(UIComponent){
			var children = UIComponent.getChildren();
			if(children.length > 0){
				for(var i=0;i<children.length;i++){
					delete tactile.foundation.UIComponent.components[children[i].id];
				}
			}
		}
		if (component instanceof tactile.foundation.UIComponent && component.parent && this.id === component.parent.id){
			try{
				removeChildren(component);
				if(this instanceof tactile.FlexView || this instanceof tactile.GridView || this instanceof tactile.View){
					var elem = this.elem.childNodes[1] || this.elem.childNodes[0];
					elem.removeChild(component.elem);
				} else {
					this.elem.removeChild(component.elem);
				}
				if (tactile.foundation.UIComponent.components[component.id]) {
					delete tactile.foundation.UIComponent.components[component.id];
				}
			} catch (e) {
				tactile.logger.error("tactile.foundation.UIComponent.remove  the component is not a child of " + this.id);
			}
		} else {
			tactile.logger.error(this.id + " Type mismatch: tactile.foundation.UIComponent.remove  expects argument of type tactile.foundation.UIComponent!");
		}
	}
});
tactile.foundation.UIContainer = function() {
	this.parent = undefined;
	this.configWidth = undefined;
	this.configHeight = undefined;
	this.configTop = undefined;
	this.configLeft = undefined;
	this.configVisible = undefined;
	this.onresize = undefined;
	this.onstatechange = undefined;
	this.init = function(elem, config) {
		tactile.foundation.UIContainer.prototype.init.call(this, elem);
		config = config || {};
		if (!config.zindex && tactile.Device.platform === 'ios' && tactile.Device.version < 4) {
			config.zindex = 1;
		}
		this.innerDiv = null;
		if(this instanceof tactile.View || this instanceof tactile.FlexView || this instanceof tactile.GridView){
			this.innerDiv = this.elem.childNodes[1];
		}
		if (config.width) {
			if (typeof config.width === "function") {
				this.configWidth = config.width;
			} else if (!isNaN(config.width)) {
				this.configWidth = function(box) {return config.width;};
			}
		}
		if (config.height) {
			if (typeof config.height === "function") {
				this.configHeight = config.height;
			} else if (!isNaN(config.height)) {
				this.configHeight = function(box) {return config.height;};
			}
		}
		if (config.top) {
			if (typeof config.top === "function") {
				this.configTop = config.top;
			} else if (!isNaN(config.top)) {
				this.configTop = function(box) {return config.top;};
			}
		}
		if (config.left) {
			if (typeof config.left === "function") {
				this.configLeft = config.left;
			} else if (!isNaN(config.left)) {
				this.configLeft = function(box) {return config.left;};
			}
		}
		if (config.visible !== undefined) {
			if (typeof config.visible === "function") {
				this.configVisible = config.visible;
			}
		}
		if (config.effect) {
			var fx = undefined;
			var slideMethod = "",fadeMethod = "", expandMethod ="", disabled = tactile.effects.Method.disabled.toLowerCase();
			if(typeof nbdevice === "object"){
				slideMethod = nbdevice.tactileslidemethod ? nbdevice.tactileslidemethod.toLowerCase() : "";
				fadeMethod = nbdevice.tactilefademethod ? nbdevice.tactilefademethod.toLowerCase() : "";
				expandMethod = nbdevice.tactileexpandmethod ? nbdevice.tactileexpandmethod.toLowerCase() : "";
			}
			if (config.effect.type && (config.effect.type == tactile.Enum.Effects.slide) && (slideMethod !== disabled)) {
				fx = new tactile.effects.Slide(this, config.effect);
			} else if (config.effect.type && (config.effect.type == tactile.Enum.Effects.expand) && (fadeMethod !== disabled)) {
				fx = new tactile.effects.Expand(this, config.effect);
			} else if (config.effect.type && (config.effect.type == tactile.Enum.Effects.fade) && (expandMethod !== disabled)) {
				fx = new tactile.effects.Fade(this, config.effect);
			}
			if(fx !== undefined){
				this.effect = fx;
				tactile.logger.info(this.toString() + ", effect: " + this.effect.toString());
			}else{
				delete config.effect;
			}
		}
		if (config.position && tactile.Enum.contains(tactile.Enum.Position, config.position)) {
			this.elem.style.position = tactile.Enum.getValue(tactile.Enum.Position, config.position);
		}
		if (config.overflow && tactile.Enum.contains(tactile.Enum.Overflow, config.overflow)) {
			this.elem.style.overflow = tactile.Enum.getValue(tactile.Enum.Overflow, config.overflow);
		}
		if (config.zindex && !isNaN(config.zindex)) {
			this.elem.style.zIndex = config.zindex;
		}
		if (config.display && tactile.Enum.contains(tactile.Enum.Display, config.display)) {
			this.elem.style.display = tactile.Enum.getValue(tactile.Enum.Display, config.display);
		}
		if (config.float && tactile.Enum.contains(tactile.Enum.Float, config.float)) {
			this.elem.style.cssFloat = tactile.Enum.getValue(tactile.Enum.Float, config.float);
		}
		if (config.opacity && !isNaN(config.opacity)) {
			this.setOpacity(config.opacity);
		}
		if(config.events){
            this.applyEvents(config.events);
        }
	};
};
tactile.foundation.UIContainer.prototype = new tactile.foundation.UIComponent();
tactile.foundation.UIContainer.extend({
	applyEvents: function(events){
	    this.attributeEvents = events;
	    var self = this;
	    for(var i in this.attributeEvents){
	        (function(index){
	            tactile.EventManager.addListener(self.id, index, function(e){
	                if(typeof self.attributeEvents[index] === "string"){
	                    try{
	                        self.attributeEvents[index] = tactile.c.U.attrValidator.validate(self.attributeEvents[index]);
	                    } catch (e){
	                        console.log(e);
	                    }
	                }
					var comp = self.attributeEvents[index];
					if (typeof comp.functionArguments[0] === 'undefined' || typeof comp.functionArguments[0] === 'object') {
					   comp.functionArguments[0] = e;
					};
					comp.component[comp.endFunction].apply(comp.component, comp.functionArguments);
	            }.bind(self));
	        })(i);
	    }
	},
	validateNumber: function(configfn, box) {
		var ret = null;
		if (configfn) {
			try {
				ret = configfn.call(this, box);
			} catch (ex) {
				tactile.logger.error(this.id + ".validateNumber(" + configfn + ", " + box + ") has thrown an exception: " + ex);
			}
			if (!isNaN(ret)) {
				return ret;
			} else {
				tactile.logger.error(this.id + ".validateNumber(" + configfn + ", " + box + ") did not return a number!");
				ret = null;
			}
		}
		return ret;
	},
	validateVisible: function() {
		var ret = true;
		if (this.configVisible) {
			try {
				ret = this.configVisible();
			} catch (ex) {
				tactile.logger.error(this.id + ".validateVisible(" + this.configVisible + ") has thrown an exception: " + ex);
				ret = true;
			}
			if (typeof ret === "boolean") {
				return ret;
			} else {
				tactile.logger.error(this.id + ".validateVisible(" + this.configVisible + ") did not return a boolean value!");
				ret = true;
			}
			if (!ret) {
				tactile.logger.warn(this.id + ".validateVisible returned false!");
			}
		}
		return ret;
	},
	getBox: function() {
		var ret = {
			top:this.elem.offsetTop,
			left:this.elem.offsetLeft,
			width:this.elem.offsetWidth,
			height:this.elem.offsetHeight
		};
		return ret;
	},
	getParentBox: function() {
		if (this.parent && this.parent.getBox) {
			return this.parent.getBox();
		} else {
			return {top:0,left:0,width:tactile.Device.screen.getWidth(),height:tactile.Device.screen.getHeight()};
		}
	},
	getParentInnerBox: function() {
		if (this.parent && this.parent.getInnerBox) {
			return this.parent.getInnerBox();
		} else {
			return {top:0,left:0,width:tactile.Device.screen.getWidth(),height:tactile.Device.screen.getHeight()};
		}
	},
	getInnerBox: function() {
		var border = {
			left:tactile.c.E.getStyleValue(this.elem,"border-left-width"),
			right:tactile.c.E.getStyleValue(this.elem,"border-right-width"),
			top:tactile.c.E.getStyleValue(this.elem,"border-top-width"),
			bottom:tactile.c.E.getStyleValue(this.elem,"border-bottom-width")
		};
		var margin = {
			left:tactile.c.E.getStyleValue(this.elem,"margin-left"),
			right:tactile.c.E.getStyleValue(this.elem,"margin-right"),
			top:tactile.c.E.getStyleValue(this.elem,"margin-top"),
			bottom:tactile.c.E.getStyleValue(this.elem,"margin-bottom")
		};
		var padding = {
			left:tactile.c.E.getStyleValue(this.elem,"padding-left"),
			right:tactile.c.E.getStyleValue(this.elem,"padding-right"),
			top:tactile.c.E.getStyleValue(this.elem,"padding-top"),
			bottom:tactile.c.E.getStyleValue(this.elem,"padding-bottom")
		};
		if (tactile.foundation.LayoutManager.loaded) {
			this.getInnerBox = function() {
				var box = this.getOuterBox();
				var ret = {
					width: (box.width - border.left - border.right - margin.left - margin.right - padding.left - padding.right),
					height: (box.height - border.top - border.bottom - margin.left - margin.bottom - padding.left - padding.right),
					left: (padding.left),
					top: (padding.top)
				};
				return ret;
			};
			return this.getInnerBox();
		}
		var box = this.getOuterBox();
		var ret = {
			width: (box.width - border.left - border.right - margin.left - margin.right - padding.left - padding.right),
			height: (box.height - border.top - border.bottom - margin.left - margin.bottom - padding.left - padding.right),
			left: (padding.left),
			top: (padding.top)
		};
		return ret;
	},
	getOuterBox: function() {
		var margin = {
				left:tactile.c.E.getStyleValue(this.elem,"margin-left"),
				right:tactile.c.E.getStyleValue(this.elem,"margin-right"),
				top:tactile.c.E.getStyleValue(this.elem,"margin-top"),
				bottom:tactile.c.E.getStyleValue(this.elem,"margin-bottom")
			};
		if (tactile.foundation.LayoutManager.loaded) {
			this.getOuterBox = function() {
				var box = this.getBox();
				var ret =  {
					width: (box.width + margin.left + margin.right),
					height: (box.height + margin.top + margin.bottom),
					left: (box.left),
					top: (box.top)
				};
				return ret;
			};
			return this.getOuterBox();
		}
		var box = this.getBox();
		var ret =  {
			width: (box.width + margin.left + margin.right),
			height: (box.height + margin.top + margin.bottom),
			left: (box.left),
			top: (box.top)
		};
		return ret;
	},
	getBoxOffset: function() {
		var margin = {
			left:tactile.c.E.getStyleValue(this.elem,"margin-left"),
			right:tactile.c.E.getStyleValue(this.elem,"margin-right"),
			top:tactile.c.E.getStyleValue(this.elem,"margin-top"),
			bottom:tactile.c.E.getStyleValue(this.elem,"margin-bottom")
		};
		var border = {
			left:tactile.c.E.getStyleValue(this.elem,"border-left-width"),
			right:tactile.c.E.getStyleValue(this.elem,"border-right-width"),
			top:tactile.c.E.getStyleValue(this.elem,"border-top-width"),
			bottom:tactile.c.E.getStyleValue(this.elem,"border-bottom-width")
		};
		var padding = {
			left:tactile.c.E.getStyleValue(this.elem,"padding-left"),
			right:tactile.c.E.getStyleValue(this.elem,"padding-right"),
			top:tactile.c.E.getStyleValue(this.elem,"padding-top"),
			bottom:tactile.c.E.getStyleValue(this.elem,"padding-bottom")
		};
		if (tactile.foundation.LayoutManager.loaded) {
			this.getBoxOffset = function() {
				var topoffset = 0;
				var leftoffset = 0;
				if (this.parent) {
					var topleftoffset = this.parent.getTopLeftOffset();
					topoffset = topleftoffset.top;
					leftoffset = topleftoffset.left;
				}
				var ret = {
					width: margin.left + margin.right + border.left + border.right + padding.left + padding.right,
					height: margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom,
					top: topoffset,
					left: leftoffset
				};
				return ret;
			};
			return this.getBoxOffset();
		}
		var topoffset = 0;
		var leftoffset = 0;
		if (this.parent) {
			var topleftoffset = this.parent.getTopLeftOffset();
			topoffset = topleftoffset.top;
			leftoffset = topleftoffset.left;
		}
		var ret = {
			width: margin.left + margin.right + border.left + border.right + padding.left + padding.right,
			height: margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom,
			top: topoffset,
			left: leftoffset
		};
		return ret;
	},
	getTopLeftOffset: function() {
		var topoffset = tactile.c.E.getStyleValue(this.elem,"padding-top");
		var leftoffset = tactile.c.E.getStyleValue(this.elem,"padding-left");
		if (tactile.foundation.LayoutManager.loaded) {
			this.getTopLeftOffset = function() {
				return {top: topoffset, left: leftoffset};
			};
			return this.getTopLeftOffset();
		}
		return {top: topoffset, left: leftoffset};
	},
	resize: function(box) {
		if (box) {
			if (box.top !== undefined && !isNaN(box.top)) {
				this.elem.style.top = box.top + tactile.Enum.Unit.px;
			} else {
				this.elem.style.top = "";
			}
			if (box.left !== undefined && !isNaN(box.left)) {
				this.elem.style.left = box.left + tactile.Enum.Unit.px;
			} else {
				this.elem.style.left = "";
			}
			if (box.width !== undefined && !isNaN(box.width)) {
				this.elem.style.width = box.width + tactile.Enum.Unit.px;
			} else {
				this.elem.style.width = "";
			}
			if (box.height !== undefined && !isNaN(box.height)) {
				this.elem.style.height = box.height + tactile.Enum.Unit.px;
			} else {
				this.elem.style.height = "";
			}
			tactile.logger.info("resize: " + this.id + " " + JSON.stringify(box));
		}
	},
	update: function(box) {
		var offset = this.getBoxOffset();
		if (box){
			if (!this.effect || (this.effect && this.state && (this.state == "initialized" || this.state == "showed" || this.state == "hidden"))){
				if(this.effect  && (this.effect instanceof tactile.effects.Slide) && (this.effect.useTransform || this.effect.useTransform3d) ){
					var bs = tactile.Device.browser.style;
					this.elem.style[bs.transitionProperty] = "";
					this.elem.style[bs.transformProperty] = "";
					this.elem.style[bs.transitionDuration] = "";
					this.elem.style[bs.transitionTimingFunction] = "";
					this.elem.style.left = "";
					this.elem.style.top = "";
					this.effect.posX = 0;
					this.effect.posY = 0;
				}
				if (box.top !== undefined && !isNaN(box.top)) {
					var nt = this.validateNumber(this.configTop, box);
					if (nt !== null) {
						this.elem.style.top = (nt + offset.top) + tactile.Enum.Unit.px;
					}
				}
				if (box.left !== undefined && !isNaN(box.left)) {
					var nl = this.validateNumber(this.configLeft, box);
					if (nl !== null) {
						this.elem.style.left = (nl + offset.left) + tactile.Enum.Unit.px;
					}
				}
				if (box.width !== undefined && !isNaN(box.width)) {
					var nw = this.validateNumber(this.configWidth, box);
					if (nw !== null) {
						this.elem.style.width = (nw - offset.width) + tactile.Enum.Unit.px;
						if(this.innerDiv){
							this.innerDiv.style.width = (nw - offset.width) + tactile.Enum.Unit.px;
						}
					} else {
						if(this.innerDiv){
							this.innerDiv.style.width = '100%';
						}
					}
				}
				if (box.height !== undefined && !isNaN(box.height)) {
					var nh = this.validateNumber(this.configHeight, box);
					if (nh !== null) {
						this.elem.style.height = (nh - offset.height) + tactile.Enum.Unit.px;
						if(this.innerDiv){
							this.innerDiv.style.height = (nh - offset.height) + tactile.Enum.Unit.px;
						}
					} else {
						if(this.innerDiv){
							this.innerDiv.style.height = '100%';
						}
					}
				}
			}
			tactile.logger.info("update: " + this.id + " " + JSON.stringify(box));
		}
		if (this.configVisible!== undefined) {
			if (this.validateVisible()) {
				this.elem.style.display = this.elem.displayStyle;
			} else {
				this.elem.style.display = tactile.Enum.Display.none;
			}
		}
	},
	append: function(element) {
		if (element instanceof tactile.foundation.UIContainer) {
			element.parent = this;
			tactile.foundation.UIComponent.prototype.append.call(this, element);
			if((element instanceof tactile.View) && element.scrollConfig != undefined && !element.scrollerInitialized){
				element.scroller = new tactile.foundation.Scroller(element, element.scrollConfig);
				element.scrollerInitialized = true;
				tactile.logger.info(element.toString() + ", scroller: " + element.scroller.toString());
			}
			element.update(element.getParentInnerBox());
			tactile.logger.info("tactile.foundation.UIContainer.append " + this.id + ", element: " + element.id);
		} else {
			tactile.logger.error(this.id + "Type mismatch: tactile.foundation.UIContainer.append  expects argument of type tactile.foundation.UIContainer!");
		}
	},
	prepend: function(element) {
		if (element instanceof tactile.foundation.UIContainer) {
			element.parent = this;
			tactile.foundation.UIComponent.prototype.prepend.call(this, element);
			if((element instanceof tactile.View) && element.scrollConfig != undefined && !element.scrollerInitialized){
				element.scroller = new tactile.foundation.Scroller(element, element.scrollConfig);
				element.scrollerInitialized = true;
				tactile.logger.info(element.toString() + ", scroller: " + element.scroller.toString());
			}
			element.update(element.getParentInnerBox());
			tactile.logger.info("tactile.foundation.UIContainer.prepend " + this.id + ", element: " + element.id);
		} else {
			tactile.logger.error(this.id + "Type mismatch: tactile.foundation.UIContainer.prepend  expects argument of type tactile.foundation.UIContainer!");
		}
	},
	clone: function(element, contentCopy) {
		var config = {},
			clone,
			cloneElement,
			scripts,
			events = {},
			targetConfig = {},
			modIDs = function(element) {
				var child, nodes = element.childNodes;
				for ( var i = 0, len = nodes.length; i < len; i++) {
					child = nodes[i];
					if ('string' == typeof child.id) {
						if (child.id != '') {
							child.id = tactile.foundation.UIComponent.getNewId();
						}
					}
					if (child.childNodes && child.childNodes.length) {
						modIDs(child);
					}
				}
			};
		config.contentCopy = contentCopy || true;
		cloneElement = element.elem.cloneNode(true);
		cloneElement.id = tactile.foundation.UIComponent.getNewId();
		scripts = cloneElement.getElementsByTagName('script');
		if (scripts.length > 0) {
			var count = scripts.length;
			for (; --count ;) {
				cloneElement.removeChild(scripts[count]);
			}
		}
		if (typeof element.configHeight != 'undefined') {
			targetConfig.height = element.configHeight(element.parent);
		}
		if (typeof element.configWidth != 'undefined') {
			targetConfig.width = element.configWidth(element.parent);
		}
		if (typeof element.configTop != 'undefined') {
			targetConfig.top = element.configTop(element.parent);
		}
		if (typeof element.configLeft != 'undefined') {
			targetConfig.left = element.configLeft(element.parent);
		}
		if (typeof element.configVisible != 'undefined') {
			targetConfig.visible = element.configVisible(element.parent);
		}
		if (element.effect) {
			var type = '';
			if (element.effect instanceof tactile.effects.Fade) {
				type = tactile.Enum.Effects.fade;
			} else if (element.effect instanceof tactile.effects.Expand) {
				type = tactile.Enum.Effects.expand;
			} else if (element.effect instanceof tactile.effects.Slide) {
				type = tactile.Enum.Effects.slide;
			}
			targetConfig.effect = {
				type : type,
				method : element.effect.getMethod(),
				duration : element.effect.duration,
				fps : element.effect.fps,
				timing : element.effect.timing
			};
		}
		if (element.attributeEvents) {
			Object.getOwnPropertyNames(element.attributeEvents).forEach(function(v,k){
				events[v] = element.attributeEvents[v].str;
			});
			targetConfig.events = events;
		}
		if (element instanceof tactile.Layout) {
			clone = new tactile.Layout(cloneElement, targetConfig);
		} else if (element instanceof tactile.View) {
			clone = new tactile.View(cloneElement, targetConfig);
		} else if (element instanceof tactile.MultiView) {
			clone = new tactile.MultiView(cloneElement, targetConfig);
		} else if (element instanceof tactile.FlexView) {
			clone = new tactile.FlexView(cloneElement, targetConfig);
		} else if (element instanceof tactile.FlexItem) {
			clone = new tactile.FlexItem(cloneElement, targetConfig);
		} else if (element instanceof tactile.GridView) {
			clone = new tactile.GridView(cloneElement, targetConfig);
		} else if (element instanceof tactile.GridItem) {
			clone = new tactile.GridItem(cloneElement, targetConfig);
		} else {
			throw tactile.errors.TypeError("tactile.foundation.UIContainer.prototype.clone","tactile.Layout, tactile.View, tactile.FlexView, tactile.FlexItem or tactile.MultiView",typeof element);
		}
		if (config.contentCopy) {
			modIDs(clone.elem);
		} else {
			while (clone.elem.hasChildNodes()) {
				cloneElement.removeChild(clone.elem.firstChild);
			}
		}
		return clone;
	},
	toString: function() {
		return "Object - tactile.foundation.UIContainer";
	}
});
tactile.foundation.LayoutManager = {
		layouts : new Array(),
		loaded : false,
		disabled : false,
		heightOffset : 68,
		init : function() {
			var me = tactile.foundation.LayoutManager;
			if(!isNaN(tactile.Device.browser.adressbarheight)){
				me.heightOffset =  tactile.Device.browser.adressbarheight;
			}
			if (me.layouts.length > 0) {
				if(tactile.Device.isTouch){
					var es = tactile.Device.screen.getHeight() + me.heightOffset;
					tactile.c.U.setBodyStyle("height", es + tactile.Enum.Unit.px);
				}
				tactile.EventManager.addOnloadListener(function() {
					me.loaded = true;
					me.updateLayout();
					window.setTimeout(function(){me.updateLayout();}, 1000);
				});
				tactile.EventManager.addResizeListener(me.updateLayout, this);
					tactile.events.TouchEventManager.layoutMode(true);
					if(!tactile.pageLayout){
						tactile.events.TouchEventManager.addListener(document, "touchmove", function(e) {
							e.browserEvent.preventDefault();
						    e.browserEvent.stopPropagation();
						});
					}
				me.updateLayout();
				if (! tactile.pageLayout) {
					if (tactile.Device.platform == "android") {
						window.setTimeout( function(){
							document.addEventListener("scroll",
							function () {
								setTimeout(function() {
									tactile.foundation.LayoutManager.updateLayout();}, 500);
							}, 2000);
						});
					}
				}
	        }
		},
		updateLayout : function() {
			var me = tactile.foundation.LayoutManager;
			if(me.disabled){ return false; }
			if (! tactile.pageLayout) {
				if(tactile.Device.isTouch){
					var es = tactile.Device.screen.getHeight() + me.heightOffset;
					tactile.c.U.setBodyStyle("height", es + tactile.Enum.Unit.px);
				}
				if(!isNaN(tactile.Device.browser.adressbarheight) && tactile.Device.browser.adressbarheight > 0){
					window.scrollTo(0, 1);
				}else if(isNaN(tactile.Device.browser.adressbarheight)){
					window.scrollTo(0, 1);
				}
			}
			for (var i = 0; i < me.layouts.length; i++) {
				me.layouts[i].update(me.layouts[i].getParentInnerBox());
			}
			tactile.c.U.updateRenderTreeFix();
		},
		addLayout : function(layout) {
			var me = tactile.foundation.LayoutManager;
			if (layout instanceof tactile.Layout ) {
				if (!me.layouts.contains(layout)) {
					me.layouts.push(layout);
				}
			} else {
				throw tactile.errors.TypeError("tactile.foundation.LayoutManager.addLayout","Layout",typeof layout);
			}
		}
};
tactile.foundation.HistoryManager = (function(){
    var HistoryManager = {
    	currentState: '',
    	isHashChange:false,
        isPushState: false,
        isHistoryApi: false,
        isUserAction: false,
        oldUrl: '',
        newUrl: '',
        storage:{},
        init:function(){
            this.isHashChange = ("onhashchange" in window);
	        this.isPushState = ("pushState" in history);
            this.isHistoryApi = this.isHashChange && this.isPushState;
            if(this.isPushState){
                window.addEventListener('popstate', this.onStateChanged.bind(this), false);
            }else if(this.isHashChange){
                window.addEventListener('hashchange', this.onHashStateChanged.bind(this), false);
            } else {
                var self = this;
                this.timer = setInterval(function(){
                    if(self.oldUrl != window.location.href){
                        if(!self.isUserAction){
                            self.runActionFromHash();
                        } else{
                            self.isUserAction = false;
                        }
                        self.oldUrl = window.location.href;
                    }
                }, 200);
            }
        },
        addHistoryState: function(stateObj, title, url){
            if(!stateObj || !stateObj.callback || !stateObj.name) return false;
            if(this.currentState == stateObj.name){
            	return false;
            }else{
            	this.currentState = stateObj.name;
            }
        	if(this.isPushState){
                window.history.pushState({callback: 'var m = ' + stateObj.callback.toString()}, (title ? title : ''), (url ? url : ''));
            }else{
                this.isUserAction = true;
                this.oldUrl = window.location.href;
                if(!window.location.hash){
                    window.location.hash = stateObj.name;
                } else {
                    window.location.hash += ('&' + stateObj.name);
                }
                if(window.sessionStorage){
                    window.sessionStorage[stateObj.name] = 'var m = ' + stateObj.callback.toString();
                } else {
                    this.storage[stateObj.name] = stateObj.callback;
                }
                this.newUrl = window.location.href;
            }
        },
        replaceHistoryState: function(stateObj, title, url){
        	if(this.isPushState){
        		if(window.history.replaceState){
        			window.history.replaceState({callback: 'var m = ' + stateObj.callback.toString()}, (title ? title : ''), (url ? url : ''));
        		}
            }else{
            	this.isUserAction = true;
                this.oldUrl = window.location.href;
                if(!window.location.hash || window.location.hash.indexOf('&') == -1){
                    window.location.replace('#' + stateObj.name);
                } else {
                	window.location.replace(window.location.hash.replace(/([^&]+)$/, stateObj.name));
                }
                if(window.sessionStorage){
                    window.sessionStorage[stateObj.name] = 'var m = ' + stateObj.callback.toString();
                } else {
                    this.storage[stateObj.name] = stateObj.callback;
                }
                this.newUrl = window.location.href;
            }
        },
        onStateChanged: function(e){
            if(e.state && e.state.callback){
                eval(e.state.callback);
                m();
            }
        },
        onHashStateChanged:function(e){
            if(!this.isUserAction){
                this.runActionFromHash.call(this);
            } else {
                this.isUserAction = false;
            }
            return false;
        },
        runActionFromHash: function(){
            if(window.location.hash){
                var hash = window.location.hash.replace('#','');
                hash = hash.split('&'), hash = hash[hash.length-1];
                if(window.sessionStorage && window.sessionStorage[hash]){
                    eval(window.sessionStorage[hash]);
                    m();
                } else {
                	if(this.storage[hash]) this.storage[hash]();
            	}
            }
        }
    };
    HistoryManager.init();
    return HistoryManager;
})();
tactile.Enum.Orientation = {
	landscape: "landscape",
	portrait: "portrait"
};
tactile.Enum.Transform = {
	auto: "auto",
	scale: "scale",
	stretch: "stretch",
	repeat: "repeat"
};
tactile.Enum.Align = {
	left: "left",
	center: "center",
	right: "right"
};
tactile.Enum.Valign = {
	top: "top",
	middle: "center",
	bottom: "bottom"
};
tactile.Enum.Clip = {
	top: "top",
	middle: "middle",
	bottom: "bottom",
	left: "left",
	center: "center",
	right: "right"
};
tactile.Enum.Cropping = {
	top: "1",
	middle: "3",
	bottom: "4",
	left: "2",
	center: "5",
	right: "6"
};
tactile.Enum.Boxing = {
	middlecenter: "1",
	topleft: "2",
	topcenter: "3",
	topright: "4",
	middleright: "5",
	bottomright: "6",
	bottomcenter: "7",
	bottomleft: "8",
	middleleft: "9"
};
tactile.Enum.Resampling = {
	box: "box",
	lanczos3: "lanczos3"
};
tactile.Enum.Encoding = {
	"default" : "default",
	base64: "base64"
};
if (typeof String.ucfirst !== 'function') {
	String.prototype.ucfirst = function () {
		var f = this.charAt(0).toUpperCase();
		return f + this.substr(1);
	};
}
tactile.foundation.Config = function(config) {
	this.init(config);
};
tactile.foundation.Config.prototype = {
	config : null,
	init : function (config) {
		this.config = config;
	},
	getConditionalBoolean : function (name) {
		this["config" + name.ucfirst()] = undefined;
		if (this.config.config[name] && typeof this.config.config[name] === "function") {
			this["config" + name.ucfirst()] = this.config.config[name];
		}
	},
	getBoolean : function (name, preset) {
		this["config" + name.ucfirst()] = preset || false;
		if (this.config.config[name]) {
			if (this.config.config[name].toUpperCase() == "TRUE") {
				this["config" + name.ucfirst()] = true;
			} else if (this.config.config[name].toUpperCase() == "FALSE") {
				this["config" + name.ucfirst()] = false;
			}
		}
	},
	getString : function (name) {
		this["config" + name.ucfirst()] = undefined;
		if (this.config.config[name]) {
			this["config" + name.ucfirst()] = this.config.config[name];
		}
	},
	getNumber : function (name, preset) {
		this["config" + name.ucfirst()] = preset || undefined;
		if (this.config.config[name] && !isNaN(this.config.config[name])) {
			this["config" + name.ucfirst()] = this.config.config[name];
		}
	},
	getOptions : function (name, preset) {
		this["config" + name.ucfirst()] = preset || [];
		if (this.config.config[name]) {
			var list = this.config.config[name].split(",");
			this["config" + name.ucfirst()] = list;
		}
	},
	getEnumerationValue : function (name, enumeration, preset) {
		this["config" + name.ucfirst()] = preset || undefined;
		if (this.config.config[name] && typeof(enumeration[String(this.config.config[name])]) != "undefined") {
			this["config" + name.ucfirst()] = enumeration[String(this.config.config[name])];
		}
	},
	getHexcolor : function (name) {
		this["config" + name.ucfirst()] = undefined;
		if (this.config.config[name]) {
			var regColorcode = /^(#)?([0-9a-fA-F]{3})([0-9a-fA-F]{3})?$/;
		    if (regColorcode.test(this.config.config[name]) != false) {
		    	this["config" + name.ucfirst()] = this.config.config[name];
		    } else {
		    	tactile.logger.error("Error - type mismatch: Config.getHexColor " + this.config.config[name]);
		    }
		}
	}
};
tactile.foundation.Config.toString = function () {
	return "tactile.foundation.Config";
};
tactile.Collection = function () {
	this.init();
};
tactile.Collection.prototype = {
	init : function () {
		this.items = [];
	},
	permitted : function (item) {
		if (item.remove != undefined)
			return true;
		throw new tactile.errors.FunctionNotSupported("Item do not have remove() Method");
	},
    getByIndex : function (index) {
        if (index >= 0) {
            return this.items[index];
        }
        throw new tactile.errors.ArrayIndexOutOfBound();
    },
    getItems : function () {
    	return this.items;
    },
    addItem : function (item) {
    	this.permitted(item);
    	this.items.push(item);
    	return this.items.length;
    },
    getLastItem : function () {
    	return this.items[this.items.length-1];
    },
    getLength : function () {
    	return this.items.length;
    },
    removeItemByIndex : function (index) {
        if (index >= 0 && this.items[index]) {
        	this.items.splice(index, 1);
        } else {
        	throw new tactile.errors.ArrayIndexOutOfBound();
        }
    },
    removeItem : function (item) {
    	this.permitted(item);
    	var at = this.items.indexOf(item);
        if (at >= 0) {
        	this.items.splice(at, 1);
        } else {
        	throw new tactile.errors.ArrayIndexOutOfBound();
        }
    },
    remove : function () {
    	for (var i=0; i<this.items.length; i++) {
    		this.items[i].remove();
    		delete this.items[i];
    	}
    	delete this.items;
    	this.items = [];
    },
    dump : function () {
    	console.dir(this.items);
    },
    toString : function () {
    	return "tactile.Collection";
    }
};
tactile.PictureCollection = function () {
	this.init();
};
tactile.PictureCollection.prototype = {
	permitted : function (item) {
		if (!(item instanceof tactile.Picture)) {
			throw new tactile.errors.TypeError("tactile.Picture.Collection._compare", "tactile.Picture", typeof item);
		}
		return true;
	}
};
tactile.PictureCollection.toString = function() {
	return "tactile.Picture.Collection";
};
tactile.c.U.extend(tactile.PictureCollection, tactile.Collection);
tactile.PictureSourceCollection = function () {
	this.init();
};
tactile.PictureSourceCollection.prototype = {
	permitted : function (item) {
		if (!(item instanceof tactile.Picture.Source)) {
			throw new tactile.errors.TypeError("tactile.Picture.Source.Collection._compare","tactile.Picture.Source", typeof item);
		}
		return true;
	},
	compare : function (media) {
		var last = { landscape : null, portrait : null, last : null };
		for (var i=0; i<this.items.length; i++) {
			if (this.items[i].compare(media)) {
				return this.items[i];
			}
			if (last["landscape"] == null)
				last["landscape"] = this.items[i];
			if (last["portrait"] == null)
				last["portrait"] = this.items[i];
			if (this.items[i].constraint.configOrientation != undefined) {
				last[this.items[i].constraint.configOrientation] = this.items[i];
			} else {
				last["landscape"] = this.items[i];
				last["portrait"] = this.items[i];
			}
		}
		return last[media.orientation];
	}
};
tactile.PictureSourceCollection.toString = function() {
	return "tactile.Picture.Source.Collection";
};
tactile.c.U.extend(tactile.PictureSourceCollection, tactile.Collection);
tactile.foundation.Observer.prototype.unsubscribeByScope = function (scope) {
	for (var i in this.fns)
		if (this.fns[i].scope == scope)
			delete this.fns[i];
};
tactile.PictureObserver = (function () {
	var Observer = function () {
		this.onload = new tactile.foundation.Observer();
		this.onfirstload = new tactile.foundation.Observer();
		this.onloadStart = new tactile.foundation.Observer();
		this.onloadEnd = new tactile.foundation.Observer();
		this.pictures = new tactile.PictureCollection();
		this.doload = {
			portrait: false,
			landscape: false
		};
		this.hasload = {
			portrait: 0,
			landscape: 0
		};
		this.firstload = [];
		this.getCnt = function () {
			return this.pictures.getLength();
		};
		this.add = function (picture) {
			if (!picture.configLazyload)
				this.pictures.addItem(picture);
		};
		this.remove = function (picture) {
			if (!picture.configLazyload)
				this.pictures.removeItem(picture);
		};
		this.informLazy = function (e) {
			this.onload.fire(e);
		},
		this.informDiligent = function (e) {
			var orientation = tactile.Device.screen.isLandscape() ? "landscape" : "portrait";
			this.hasload[orientation]++;
			if (this.doload[orientation] === false || this.doload[orientation] < this.getCnt())
				this.onloadStart.fire({cnt: this.getCnt(), orientation: orientation});
			this.doload[orientation] = this.getCnt();
			this.onload.fire(e);
			if (this.firstload.indexOf(e.source.oid) < 0) {
				this.onfirstload.fire(e);
				this.firstload.push(e.source.oid);
			}
			if (this.hasload[orientation] == this.getCnt())
				this.onloadEnd.fire({cnt: this.getCnt(), orientation: orientation});
		},
		this.inform = function (e) {
			if (!e.source.configLazyload)
				this.informDiligent(e);
			else
				this.informLazy(e);
		};
		this.toString = function () {
			return "tactile.PictureObserver";
		}
	};
	return new Observer();
})();
tactile.PictureStore = (function () {
	var Store = function () {
		this.images = {};
		this.raw = [];
		this.getByOid = function (oid) {
			for (var i in this.images) {
				if (this.images[i].oid == oid) {
					return this.images[i];
				}
			}
			return undefined;
		},
		this.remove = function (source) {
			if (this.images[source.oid] != undefined) {
				if (this.images[source.oid].img.src != "")
					this.raw.push(this.images[source.oid].img);
				delete this.images[source.oid];
			}
		},
		this.add = function (source, box, url) {
			for (var i=0;i<this.images.length; i++) {
				if (this.images[i].serialized == source.serialized
					&& (this.images[i].naturalWidth >= box.width
						|| this.images[i].naturalHeight >= box.height)) {
					tactile.PictureObserver.inform({img: this.images[i], error: false});
					return;
				}
			}
			var that = this;
			(function (source) {
				source.img = new Image();
				source.img.url = url;
				source.img.addEventListener("load", function (e) {
					that.images[source.oid] = source;
					tactile.PictureObserver.inform({source: source, error: false});
				});
				source.img.addEventListener("error", function (e) {
					tactile.PictureObserver.inform({source: source, error: true, message: "404 Not Found"});
				});
				switch (source.configEncoding) {
					case tactile.Enum.Encoding.base64:
						var xhr = tactile.c.A.getXHRObject();
						xhr.onreadystatechange = function(e) {
							if (this.readyState == 4) {
								if (this.status == 200) {
									source.img.src = this.responseText;
								} else {
									tactile.PictureObserver.inform({source: source, error: true, message: this.status + " " + this.statusText});
								}
							}
						};
						xhr.onerror = function () {
							tactile.PictureObserver.inform({source: source, error: true, message: this.status + " " + this.statusText});
						};
						xhr.open("GET", url, true);
						xhr.send();
					break;
					default:
						source.img.src = url;
					break;
				}
			})(source);
		};
		this.toString = function () {
			return "tactile.PictureStore";
		}
	};
	return new Store();
})();
tactile.Picture = function (elem, config) {
	this.config = null;
	this.parent = null;
	this.collection = null;
	this.interupt = false;
	this.current = null;
	this.img = null;
	this.media = null;
	this.timer = null;
	this.timer2 = null;
	this.dom = {back: null, pic: null, caption: null, front: null};
	config = config || {};
	this.config = new tactile.foundation.Config(config);
	if (config.parent) {
		if (config.parent instanceof tactile.foundation.UIContainer === true) {
			this.parent = config.parent;
		} else {
			tactile.logger.error("Error - type mismatch: Picture " + this.id + " expects parent of type UIContainer!");
		}
	}
	tactile.Picture.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.config.getString.call(this, "alt");
	this.config.getString.call(this, "title");
	this.config.getString.call(this, "caption");
	this.config.getString.call(this, "opacity");
	this.config.getString.call(this, "href");
	this.config.getString.call(this, "tel");
	this.config.getString.call(this, "loadingmask");
	this.config.getBoolean.call(this, "lazyload", false);
	this.media = new tactile.Picture.MediaCondition(config);
	this.media.onupdate.subscribe(this.updateimage, this);
	if (this.parent && this.parent.getBox) {
		this.parent.onresize.subscribe(this.update, this);
	}
	this.onresize.subscribe(this.media.update, this.media);
	if (!(config.source instanceof Array)) {
		throw new tactile.errors.ArgumentUndefined("tactile.Picture.init","config.source");
	}
	this.collection = new tactile.PictureSourceCollection();
	for (var index=0; index<config.source.length; index++) {
		config.source[index].lazyload = this.configLazyload ? "true" : "false";
        this.collection.addItem(new tactile.Picture.Source(config.source[index], this));
    }
	this.initdom();
	tactile.PictureObserver.add(this);
	tactile.PictureObserver.onload.subscribe(function (e) {
		if (this.current
				&& e.source.oid
				== this.current.oid) {
			this.interupt = false;
			tactile.c.E.removeClass(this.elem, this.configLoadingmask);
			this.finalize(e);
		}
	}, this);
};
tactile.Picture.prototype = new tactile.foundation.UIContainer();
tactile.Picture.timeout = 1010;
tactile.Picture.extend({
	initdom : function () {
		for (var i in this.dom) {
			if (i == "caption" && !this.configCaption)
				continue;
			if (i == "pic" && (this.configHref || this.configTel)) {
				this.dom[i] = document.createElement("a");
			} else {
				this.dom[i] = document.createElement("div");
			}
			this.dom[i].className = i;
			if (i == "back" || i == "front") {
				this.dom[i].style.position = "absolute";
			}
			this.dom[i].style.display= "block";
			this.dom[i].style.top    = "0px";
			this.dom[i].style.left   = "0px";
			this.dom[i].style.width  = "100%";
			this.dom[i].style.height = "100%";
			if (i == "front") {
				this.dom[i].style["display"] = "none";
			}
		}
		if (this.configHref) {
			this.dom.pic.setAttribute("href", this.configHref);
		}
		if (this.configTitle) {
			this.elem.setAttribute("title", this.configTitle);
		}
		if (this.configOpacity) {
			this.dom.pic.style.opacity = (this.configOpacity / 100);
		}
		if (this.configCaption) {
			if (this.configHref) {
				this.dom.caption = document.createElement("a");
				this.dom.caption.setAttribute("href", this.configHref);
			} else {
				this.dom.caption = document.createElement("span");
			}
			this.dom.caption.className = "caption";
			this.dom.caption.innerHTML = this.configCaption.replace("\n", "<br/>");
			this.elem.appendChild(this.dom.caption);
		}
		for (var i in this.dom) {
			if (this.dom[i] != null) {
				this.elem.appendChild(this.dom[i]);
			}
		}
		if (this.configLoadingmask) {
			if (tactile.c)
				tactile.c.E.addClass(this.elem, this.configLoadingmask);
			else
				tactile.c.E.addClass(this.elem, this.configLoadingmask);
		}
	},
	update : function (box) {
		if (box.width < 1 || box.height < 1)
			return;
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		if (nbdevice.devicetype != "Desktop-Browser"
			|| ( tactile.Picture.force_device_type != null && tactile.Picture.force_device_type != "Desktop-Browser")) {
			if (this.timer)
				window.clearTimeout(this.timer);
			this.timer = window.setTimeout((function () {
				var newbox = this.getInnerBox();
				this.onresize.fire(newbox);
			}).bind(this), tactile.Picture.timeout);
		} else {
			var newbox = this.getInnerBox();
			this.onresize.fire(newbox);
		}
		this.resize();
	},
	updateimage : function (media) {
		if (this.interupt)
			return;
		var match = this.collection.compare(media);
		if (!this.current || this.current.oid != match.oid) {
			this.interupt = true;
			if (this.current) {
				this.dom.front.style["display"] = "block";
				if (this.configLoadingmask != undefined) tactile.c.E.addClass(this.elem, this.configLoadingmask);
			}
			match.fetch(media, this);
		}
	},
	finalize : function (e) {
		this.img = e.source;
		if (this.timer2) {
			window.clearTimeout(this.timer2);
		}
		else if (this.configLoadingmask != undefined) {
			tactile.c.E.removeClass(this.elem, this.configLoadingmask);
		}
		this.dom.front.style["display"] = "none";
		this.dom.back.style["display"]  = "none";
		tactile.c.E.removeClass(this.dom.pic, "error");
		this.dom.pic.innerHTML = "";
		if (e.error == false) {
			this.dom.pic.style["backgroundImage"] = "url("+this.img.img.src+")";
			this.dom.pic.style["backgroundPosition"] = this.img.configAlign + " " + this.img.configValign;
		} else {
			tactile.c.E.addClass(this.dom.pic, "error");
			this.dom.pic.innerHTML = "<span>Error: " + e.message + "</span>";
		}
		this.resize();
	},
	resize: function () {
		if (!this.img)
			return;
		var offsets = this.getInnerBox();
		if (this.configHeight == undefined) {
			var width  = (offsets.width >= this.img.img.naturalWidth ? this.img.img.naturalWidth : offsets.width);
			var height = this.img.img.naturalHeight * (width/this.img.img.naturalWidth);
			this.dom.pic.style["height"] = height + "px";
		}
		if (this.configWidth == undefined) {
			var height = (offsets.height >= this.img.img.naturalHeight ? this.img.img.naturalHeight : offsets.height);
			var width  = this.img.img.naturalWidth * (height/this.img.img.naturalHeight);
			this.dom.pic.style["width"] = width + "px";
		}
		if (this.img.configTransform == tactile.Enum.Transform.repeat) {
			this.dom.pic.style["backgroundSize"] = this.img.img.width + "px " + this.img.img.height + "px";
			this.dom.pic.style["backgroundRepeat"] = "repeat";
			return;
		}
		this.dom.pic.style["backgroundRepeat"] = "no-repeat";
		if (this.img.configTransform == tactile.Enum.Transform.stretch) {
			this.dom.pic.style["backgroundSize"] = "100% 100%";
			return;
		}
		if (this.img.configClip) {
			switch (this.img.configClip) {
				case tactile.Enum.Clip.top:
					this.dom.pic.style["backgroundPosition"] = "left top";
				break;
				case tactile.Enum.Clip.middle:
					this.dom.pic.style["backgroundPosition"] = "left center";
				break;
				case tactile.Enum.Clip.bottom:
					this.dom.pic.style["backgroundPosition"] = "left bottom";
				break;
				case tactile.Enum.Clip.left:
					this.dom.pic.style["backgroundPosition"] = "left top";
				break;
				case tactile.Enum.Clip.center:
					this.dom.pic.style["backgroundPosition"] = "center top";
				break;
				case tactile.Enum.Clip.right:
					this.dom.pic.style["backgroundPosition"] = "right top";
				break;
			}
			this.dom.pic.style["backgroundSize"]
				= (this.img.img.width / this.img.constraint.configMinratio) + "px "
				+ (this.img.img.height / this.img.constraint.configMinratio) + "px";
			return;
		}
		var ima1 = {};
		ima1.width  = (offsets.width >= this.img.img.naturalWidth ? this.img.img.naturalWidth : offsets.width);
		ima1.height = this.img.img.naturalHeight * (ima1.width/this.img.img.naturalWidth);
		var ima2 = {};
		ima2.height = (offsets.height >= this.img.img.naturalHeight ? this.img.img.naturalHeight : offsets.height);
		ima2.width  = this.img.img.naturalWidth * (ima2.height/this.img.img.naturalHeight);
		if (offsets.height >= ima1.height) {
			this.dom.pic.style["backgroundSize"] = ima1.width + "px " + ima1.height + "px";
		} else {
			this.dom.pic.style["backgroundSize"] = ima2.width + "px " + ima2.height + "px";
		}
	},
	remove : function () {
		this.parent.onresize.unsubscribeByScope(this);
		this.onresize.unsubscribeByScope(this.media);
        this.collection.remove();
        delete this.collection;
        for (var i in  this.dom)
        	this.elem.removeChild(this.dom[i]);
        tactile.PictureObserver.remove(this);
        tactile.PictureObserver.onload.unsubscribeByScope(this);
		if (tactile.foundation.UIComponent.components[this.id]) {
			delete tactile.foundation.UIComponent.components[this.id];
		}
		this.elem.parentNode.removeChild(this.elem);
		delete this;
	},
	dump : function () {
		console.dir(this);
	}
});
tactile.Picture.toString = function() {
	return "tactile.Picture";
};
tactile.Picture.MediaCondition = function (config) {
	this.config = new tactile.foundation.Config(config);
	this.init();
};
tactile.Picture.MediaCondition.prototype = {
	init : function () {
		this.lastOrientation = undefined;
		this.config.getOptions.call(this, "reload");
		this.rules = {
			orientationchange : false,
			reload : true
		};
		if (this.configReload instanceof Array) {
			for (var i=0; i<this.configReload.length; i++) {
				if (this.configReload[i].toLowerCase().indexOf("true") >= 0) {
					this.rules.reload = true;
				}
				if (this.configReload[i].toLowerCase().indexOf("false") >= 0) {
					this.rules.reload = false;
				}
				if (this.configReload[i].toLowerCase().indexOf("orientation") >= 0) {
					this.rules.orientationchange = true;
				}
			}
		}
		this.onupdate = new tactile.foundation.Observer();
		this.dpr = 1;
		if (window.devicePixelRatio !== undefined) {
			this.dpr = window.devicePixelRatio;
		}
	},
	update : function (box) {
		if (this.lastOrientation && !this.rules.orientationchange && !this.rules.reload)
			return;
		var orientation = tactile.Device.screen.isLandscape() ? "landscape" : "portrait";
		if (this.lastOrientation == orientation && !this.rules.reload)
			return;
		if ((this.lastOrientation == orientation || !this.rules.orientationchange) && !this.rules.reload)
			return;
		var media = {
			width: box.width,
			height : box.height,
			orientation : orientation,
			ratio: this.dpr
		};
		this.lastOrientation = orientation;
		this.onupdate.fire(media);
	}
};
tactile.Picture.MediaCondition.toString = function () {
	return "tactile.Picture.MediaCondition";
}
tactile.Picture.MediaConstraint = function (config) {
	this.init(config);
};
tactile.Picture.MediaConstraint.prototype = {
	init : function (config) {
		this.config = new tactile.foundation.Config(config);
		this.config.getNumber.call(this, "width", 10000);
		this.config.getNumber.call(this, "height", 10000);
		this.config.getEnumerationValue.call(this, "orientation", tactile.Enum.Orientation);
		this.config.getNumber.call(this, "minratio", 1);
	},
	compare : function (media) {
		if (this.configOrientation == media.orientation || this.configOrientation == undefined) {
			if (media.width <= this.configWidth || this.configWidth == undefined) {
				if (media.height <= this.configHeight || this.configHeight == undefined) {
					if (media.ratio >= this.configMinratio || this.configMinratio == undefined) {
						return true;
					}
				}
			}
		}
		return false;
	}
};
tactile.Picture.MediaConstraint.toString = function () {
	return "tactile.Picture.MediaConstraint";
}
tactile.Picture.Source = function (config, parent) {
	this.init(config, parent);
};
tactile.Picture.Source.idSeed = 0;
tactile.Picture.Source.createOId = function() {
	return "OID_" + (tactile.Picture.Source.idSeed += 1);
};
tactile.Picture.Source.prototype = {
	config : null,
	parent : null,
	constraint : null,
	oid: null,
	serialized: null,
	img : null,
	init : function (config, parent) {
		this.config = new tactile.foundation.Config(config);
		this.config.getString.call(this, "src");
		if (!this.configSrc)
			throw new tactile.errors.ArgumentUndefined("tactile.Picture.Source.init","config.src");
		this.constraint = new tactile.Picture.MediaConstraint(config);
		this.oid = tactile.Picture.Source.createOId();
		this.parent = parent;
		this.config.getEnumerationValue.call(this, "transform",  tactile.Enum.Transform, tactile.Enum.Transform.auto);
		this.config.getEnumerationValue.call(this, "align", tactile.Enum.Align, tactile.Enum.Align.left);
		this.config.getEnumerationValue.call(this, "valign", tactile.Enum.Valign, tactile.Enum.Valign.top);
		this.config.getEnumerationValue.call(this, "clip", tactile.Enum.Clip);
		this.config.getString.call(this, "format");
		this.config.getNumber.call(this, "fsize", 999999);
		this.config.getEnumerationValue.call(this, "cropping", tactile.Enum.Cropping);
		this.config.getEnumerationValue.call(this, "boxing", tactile.Enum.Boxing);
		this.config.getHexcolor.call(this, "fillcolor");
		this.config.getNumber.call(this, "prop");
		this.config.getNumber.call(this, "quality");
		this.config.getEnumerationValue.call(this, "resampling", tactile.Enum.Resampling, tactile.Enum.Resampling.box);
		this.config.getEnumerationValue.call(this, "encoding", tactile.Enum.Encoding, tactile.Enum.Encoding["default"]);
		this.config.getBoolean.call(this, "lazyload", false);
		this.serialize();
	},
	getParam : function (scope, name) {
		if (this["config" + name.ucfirst()]) {
			scope[name] = this["config" + name.ucfirst()];
			return true;
		}
		return false;
	},
	serialize : function () {
		var obj =  {};
		if (!this.getParam(obj, "cropping"))
			this.getParam(obj, "boxing");
		this.getParam(obj, "format");
		this.getParam(obj, "fsize");
		this.getParam(obj, "fillcolor");
		this.getParam(obj, "prop");
		this.getParam(obj, "quality");
		this.getParam(obj, "resampling");
		this.getParam(obj, "encoding");
		obj.url = this.configSrc.replace("&", "%26");
		var joined = [];
		for (var i in obj) {
			joined.push(i + "=" + obj[i]);
		}
		this.serialized = joined.join("&");
		return this.serialized;
	},
	compare : function (media) {
		if (this.constraint.compare(media))
			return this;
		return false;
	},
	getBoxUrl : function (box) {
		box.height = box.height * this.constraint.configMinratio;
		box.width = box.width * this.constraint.configMinratio;
		if (~"top|middle|bottom".indexOf(this.configClip)) {
			box.height = 100000;
		} else if (~"left|center|right".indexOf(this.configClip)) {
			box.width = 100000;
		}
		return "?width="
		   + box.width
		   + "&height="
		   + box.height;
	},
	getFixedUrl : function () {
		return "?width="
		   + this.constraint.configWidth * this.constraint.configMinratio
		   + "&heigth="
		   + this.constraint.configHeight * this.constraint.configMinratio;
	},
	getUrl : function (box) {
		var url = nbdevice.icpath;
		if (nbdevice.devicetype == "Desktop-Browser"
			&&
			( tactile.Picture.force_device_type == null
			  || tactile.Picture.force_device_type == "Desktop-Browser")) {
			url += this.getFixedUrl();
		} else {
			url += this.getBoxUrl(box);
		}
		url += "&" + this.serialized;
		return url;
	},
	fetch : function (media, scope) {
		if (scope)
			scope.current = this;
		var url = this.getUrl({width: media.width, height: media.height});
		tactile.PictureStore.add(this, media, url);
	},
	remove : function () {
		tactile.PictureStore.remove(this);
	}
};
tactile.Picture.Source.toString = function() {
	return "tactile.Picture.Source";
};
tactile.AjaxLoader = function(config){
    config = config || {};
	this.onstart = new tactile.foundation.Observer();
	this.onsuccess = new tactile.foundation.Observer();
    this.onerror = new tactile.foundation.Observer();
    this.timeout = 60000;
	this.responseUID = tactile.c.U.getUID();
    this.customErrors = config.errors || {};
	this.url = config.url;
	this.method = config.method || 'get';
	this.parameters = config.params || '';
    this.isJSONP = config.isJSONP || false;
	this.xhr = null;
};
tactile.AjaxLoader.JsonpRegistrator = {};
tactile.AjaxLoader.extend({
	getResponseText: function(){
	    if (this.xhr !== null) {
			return this.xhr.responseText;
		} else {
			return null;
		}
	},
	getResponseXML: function(){
	    if (this.xhr !== null) {
			return this.xhr.responseXML;
		} else {
			return null;
		}
	},
	load: function(config){
		var requestTimer = undefined;
	    this.url = config.url || this.url;
		this.method = config.method || this.method;
		this.onstart.fire({status: 0, message: "", url: this.url, loader: this});
		this.parameters = config.params || this.parameters;
		if (this.parameters.length > 0 && this.method.toUpperCase() == 'GET') {
			this.url = (this.url.indexOf("?") >= 0) ? this.url + "&" : this.url + "?";
			this.url += this.parameters;
		}
	    this.isJSONP = config.isJSONP || this.isJSONP;
	    this.parameters = (this.parameters.length > 0) ? (this.parameters + "&") : this.parameters;
		if(tactile.Device.browser.canCookie){
			this.parameters += "emvcc=1";
		}else{
			this.parameters += "emvcc=0";
		}
		this.parameters += "&disablejsd=true";
		if(this.url.indexOf("?") >= 0) {
			var tmp = this.url.split("?");
			if(tmp.length >0){
				this.url = tmp[0] + tactile.c.A.getSessionID() + "?";
				if(tmp[1]){
					this.url = this.url + tmp[1];
				}
			}
			this.url = this.url + "&";
		} else {
			this.url = this.url + tactile.c.A.getSessionID() + "?";
		}
		if (this.method.toUpperCase() == 'GET') {
			if (this.url.indexOf("?") >= 0) {
				this.url = this.url + "&";
			} else {
				if (this.parameters.length > 0) {
					this.url = this.url + "?";
				}
			}
			this.url += this.parameters;
		}
	    if(this.isJSONP){
	        this.createJsonp();
	        return false;
	    }
	    this.xhr = new tactile.c.A.getXHRObject();
	    this.xhr.onreadystatechange = (function() {
			try{
				if (this.xhr.readyState == 4) {
					clearTimeout(requestTimer);
					if (this.xhr.status == 200) {
						var stat = this.getStatus();
						this.onsuccess.fire({status: stat.status, message: stat.message, url: this.url, loader: this});
					} else {
	                    var stat = this.getStatus();
						this.onerror.fire({status: stat.status, message: stat.message, url: this.url, loader: this});
					}
				}
			}catch(e){
				this.onerror.fire({status: e.number, message: e.message, url: this.url, loader: this});
			}
		}).bind(this);
		try{
			this.xhr.open(this.method, this.url, true);
			var self = this;
			requestTimer = setTimeout(function() {
				self.xhr.abort();
				self.onerror.fire({status: "408", message: "Request Time-out", url: self.url, loader: self});
			}, self.timeout);
		}catch(e){
			this.onerror.fire({status: e.number, message: e.message, url: this.url, loader: this});
		}
		this.xhr.setRequestHeader("Cache-Control", "no-transform");
		this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	    if (this.method.toUpperCase() == 'GET') {
			this.xhr.send(null);
		} else {
			this.xhr.send(this.parameters);
		}
	},
	createJsonp: function(){
	    try {
	        this.onstart.fire({status: 'jsonp', message: 'request started', url: this.url, loader: this});
	        var script = document.createElement("script"),
	        responce = null,
	        starttime = (new Date()).getTime();
	        script.onload = function(){
	            this.onsuccess.fire({status: 'success', message: 'request time: '+ (starttime - (new Date()).getTime()), url: this.url, response: responce});
	        }.bind(this);
	        script.onerror = (function(){
	            this.onerror.fire({message: arguments[0], url: this.url, loader: this});
	        }).bind(this);
	        tactile.AjaxLoader.JsonpRegistrator.AjaxLoader = function(){
	            responce = arguments;
	        };
	        script.type = 'text/javascript';
	        script.src = this.url + "?"+ (this.parameters != "" ? this.parameters + "&" : "")+ 'callback=tactile.AjaxLoader.JsonpRegistrator.AjaxLoader';
	        document.getElementsByTagName("head")[0].appendChild(script);
	    }catch(e){
	        this.onerror.fire({status: e.number, message: e.message, url: this.url, loader: this});
	    }
	},
	stop: function(){
	    if (this.xhr !== null) {
			this.xhr.abort();
			this.onerror.fire({status: "0", message: "Request stopped", url: this.url, loader: this});
		}
	},
	getStatus: function(){
	    return (this.customErrors[this.xhr.status]) ? {
			status : this.xhr.status,
			message : this.customErrors[this.xhr.status]
		} : {
			status : this.xhr.status,
			message : this.xhr.statusText
		};
	},
	setCustomErrors: function(customErrors){
	    this.customErrors = customErrors;
	}
});
tactile.ContentLoader = function(config) {
	config = config || {};
	this.onstart = new tactile.foundation.Observer();
	this.onsuccess = new tactile.foundation.Observer();
	this.onerror = new tactile.foundation.Observer();
	this.timeout = 60000;
	this.responseUID = tactile.c.U.getUID();
	var private_url = "";
	this.setUrl = function(url) {
		if (url) {
			if (url.indexOf("http") == 0) {
				throw new tactile.errors.InvalidArgument("tactile.ContentLoader","config.url");
			} else {
				private_url = url;
			}
		}
		return private_url;
	};
	this.scriptInitialized = false;
	this.loadedComponents = [];
	this.customErrors = config.errors || {};
	this.url = this.setUrl(config.url);
	this.method = config.method || 'get';
	this.parameters = config.params || '';
	this.fragmentId = config.fragment;
	this.xhr = null;
};
tactile.ContentLoader.prototype = new tactile.AjaxLoader();
tactile.ContentLoader.extend({
	removeResponseContainer: function(){
		var rsp = document.getElementById(this.responseUID);
		if(rsp){
			rsp.parentNode.removeChild(rsp);
		}
	},
	load: function(config) {
		var requestTimer = undefined,page;
		config = config || {};
		this.xhr = tactile.c.A.getXHRObject();
		this.url = this.setUrl(config.url);
		this.method = config.method || this.method;
		this.onstart.fire({status: 0, message: "", url: this.url, loader: this});
		this.parameters = config.params || this.parameters;
		if (config.fragment) {
			this.fragmentId = config.fragment;
		}
		if (this.fragmentId) {
			if (this.parameters.length > 0) {
				this.parameters += "&";
			}
			this.parameters += "nbbiscuitid=" + this.fragmentId;
		}
		this.parameters = (this.parameters.length > 0) ? (this.parameters + "&") : this.parameters;
		if(tactile.Device.browser.canCookie){
			this.parameters += "emvcc=1";
		}else{
			this.parameters += "emvcc=0";
		}
		this.parameters += "&disablejsd=true";
		if(this.url.indexOf("?") >= 0) {
			var tmp = this.url.split("?");
			if(tmp.length >0){
				this.url = tmp[0] + tactile.c.A.getSessionID() + "?";
				if(tmp[1]){
					this.url = this.url + tmp[1];
				}
			}
			this.url = this.url + "&";
		} else {
			this.url = this.url + tactile.c.A.getSessionID() + "?";
		}
		if (this.method.toUpperCase() == 'GET') {
			if (this.url.indexOf("?") >= 0) {
				this.url = this.url + "&";
			} else {
				if (this.parameters.length > 0) {
					this.url = this.url + "?";
				}
			}
			this.url += this.parameters;
		}
		var self = this;
		this.xhr.onreadystatechange = function() {
			try{
				if (self.xhr.readyState == 4) {
					clearTimeout(requestTimer);
					if (self.xhr.status == 200) {
						self.scriptInitialized = false;
						self.loadedComponents = [];
						var stat = self.getStatus();
						self.onsuccess.fire({status: stat.status, message: stat.message, url: self.url, loader: self});
					} else {
						var stat = self.getStatus();
						self.onerror.fire({status: stat.status, message: stat.message, url: self.url, loader: self});
					}
				}
			}catch(e){
				self.onerror.fire({status: e.number, message: e.message, url: self.url, loader: self});
			}
		};
		try{
			page = tactile.c.A.getRootPath() + this.url;
			this.xhr.open(this.method, page, true);
			requestTimer = setTimeout(function() {
				self.xhr.abort();
				self.onerror.fire({status: "408", message: "Request Time-out", url: self.url, loader: self});
			}, self.timeout);
		}catch(e){
			self.onerror.fire({status: e.number, message: e.message, url: self.url, loader: self});
		}
		this.xhr.setRequestHeader("Cache-Control", "no-transform");
		this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		if (this.method.toUpperCase() == 'GET') {
			this.xhr.send(null);
		} else {
			this.xhr.send(this.parameters);
		}
	},
	getComponent: function(key) {
		var ret = null;
		var comp = this.getComponents();
		for (var i = 0; i < comp.length; i++) {
			if (comp[i].id == key) {
				ret = comp[i];
			}
		}
		return ret;
	},
	evaluate: function(args) {
		var responseDom = args.shift();
		var filter = [];
		if (args && args.length > 0) {
			for (var j = 0; j < args.length; j++) {
				filter.push(args[j].toString());
			}
		} else {
			filter = [tactile.Picture.toString(), tactile.View.toString(), tactile.MultiView.toString(), tactile.FlexView.toString(), tactile.Layout.toString(), tactile.FlexItem.toString(), tactile.GridView.toString(), tactile.GridItem.toString()];
		}
		var responseContainer, scripts;
		this.removeResponseContainer();
		responseContainer = document.createElement("div");
		responseContainer.setAttribute("id", this.responseUID);
		responseContainer.style.display = "none";
		responseContainer.appendChild(responseDom);
		document.body.appendChild(responseContainer);
		tactile.c.U.fixLinks();
		if (responseContainer) {
			scripts = responseContainer.getElementsByTagName("script");
			for (var i = 0; i < scripts.length; i++){
				var script = scripts[i].innerHTML;
				var found = false;
				filter.forEach(function(el) { if (script.indexOf(el) > -1) {found=true;}});
				if (found) {
					var compid = scripts[i].parentNode.id;
					if (compid.indexOf("nbautoid") > -1 && tactile.page.isComponent(compid)) {
						compid = tactile.foundation.UIComponent.getNewId();
						script = script.replace(scripts[i].parentNode.id, compid);
						scripts[i].parentNode.id = compid;
					}
					if (! tactile.page.isComponent(scripts[i].parentNode.id)) {
						try {
							script = script.replace(/\n/gim, "");
							script = script.replace(/<!--/gim, "");
							script = script.replace(/-->/gim, "");
							script = script.replace(/tactile.\page\.init\s*?\(function\s*?\(\)\s*?\{(.*?)\}\);$/gim, "$1");
							var newcomp = eval(script);
							this.loadedComponents.push(newcomp);
						} catch (ex) {
							tactile.logger.error("ContentLoader.getComponents: initialization of remote component " + scripts[i].parentNode.id + " has thrown an exception: " + ex.message);
						}
					} else {
						tactile.logger.warn("ContentLoader.getComponents: component " + scripts[i].parentNode.id + " allready exists!");
					}
				}
			}
		}
	},
	getComponents: function() {
		var args = Array.prototype.slice.call(arguments);
		if(!this.scriptInitialized && this.xhr !== null) {
			args.unshift(this.xhr.getResponseDom());
			this.evaluate(args);
			this.scriptInitialized = true;
		}
		return this.loadedComponents;
	}
});
tactile.ContentLoaderCached = function(config) {
	this.parent = new tactile.ContentLoader(config);
	this.scriptInitialized = undefined;
	this.workon = undefined;
	this.cacheid = undefined;
	this.cache = {};
	this.onstart = new tactile.foundation.Observer();
	this.parent.onstart.subscribe((function (e) {
		this.onstart.fire(e);
	}).bind(this));
	this.onerror = new tactile.foundation.Observer();
	this.parent.onerror.subscribe((function (e) {
		this.onerror.fire(e);
	}).bind(this));
	this.onstatechange = new tactile.foundation.Observer();
	this.onsuccess = new tactile.foundation.Observer();
	this.canCache = typeof(Storage) == "undefined" ? false : true;
	if (this.canCache) {
		if (localStorage.getItem("ContentLoaderCached") == null) {
			try {
				localStorage.setItem("ContentLoaderCached", JSON.stringify(this.cache));
			} catch (e) {
				this.canCache = false;
			}
		} else {
			var cache = localStorage.getItem("ContentLoaderCached");
			this.cache = JSON.parse(cache);
		}
	}
	if (!this.canCache) {
		this.parent.onsuccess.subscribe((function (e) {
			this.onsuccess.fire(e);
		}).bind(this));
	} else {
		this.parent.onsuccess.subscribe((function (e) {
			this.setCache(e);
		}).bind(this));
	}
};
tactile.ContentLoaderCached.extend({
	getComponent : tactile.ContentLoader.prototype.getComponent,
	statechange : function(e) {
		this.onstatechange.fire(e);
	},
	str2hash : function (str) {
		var hash1 = (5381<<16) + 5381;
		var hash2 = hash1;
		var hashPos = 0;
		while(hashPos < str.length) {
		    hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ str.charCodeAt(hashPos);
		    if( hashPos == str.length - 1) {
		        break;
		    }
		    hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ str.charCodeAt(hashPos + 1);
		    hashPos += 2;
		}
		return hash1 + (hash2 * 1566083941);
	},
	success : function() {
		this.onsuccess.fire({
			"loader": this,
			"message": "",
			"status" : 0,
			"url": this.workon.url
		});
	},
	getTime : function() {
		return Math.round(new Date().getTime() / 1000);
	},
	getCacheId : function(config) {
		if (typeof(config.method) == "undefined")
			config.method = "get";
		if (typeof(config.params) == "undefined")
			config.params = [];
		var cacheId = config.method + ";" + config.url;
		for (var i in config.params)
			cacheId += "&" + i + "=" + config.params[i];
		return this.str2hash(cacheId);
	},
	getComponents : function() {
		if (!this.canCache)
			return this.parent.getComponents.apply(arguments);
		var args = Array.prototype.slice.call(arguments);
		if(!this.scriptInitialized && this.cache[ this.getCacheId(this.workon) ] !== undefined) {
			var dom = document.createElement("div");
			dom.innerHTML = JSON.parse(localStorage.getItem("clc" + this.workon.uid));
			args.unshift(dom);
			this.parent.evaluate(args);
			this.scriptInitialized = true;
		}
		return this.parent.loadedComponents;
	},
	getCache : function() {
		var cacheId = this.getCacheId(this.workon);
		if (this.cache[cacheId]) {
			this.workon = this.cache[cacheId];
			var validUntil = parseInt(this.workon.validUntil);
			if (!validUntil)
				this.statechange({ message: "cache found, valid forever", data: cacheId, method: "getCache" });
			else {
				var expire = validUntil - this.getTime();
				if (expire > 0) {
					this.statechange({ message: "cache found, expires in " + expire + "s", data: cacheId, method: "getCache" });
				} else {
					this.statechange({ message: "cache found, expired " + expire + "s ago", data: cacheId, method: "getCache" });
					this.unsetCache(this.workon);
					return null;
				}
			}
			this.success();
			return true;
		}
		this.statechange({ message: "cache not found", data: cacheId, method: "getCache" });
		return false;
	},
	setCache : function(e) {
		if (e.loader.xhr !== null) {
			localStorage.setItem(
				"clc" + this.workon.uid,
				JSON.stringify(document.createElement("div").appendChild(e.loader.xhr.getResponseDom()).innerHTML)
			);
			this.statechange({ message: "cache created", data: "clc" + this.workon.uid, method: "setCache" });
			this.success();
			return true;
		}
		return false;
	},
	initCache : function() {
		if (typeof(this.workon.expireAt) == "undefined")
			this.workon.validUntil = false;
		else
			this.workon.validUntil = this.getTime() + parseInt(this.workon.expireAt);
		this.workon.uid = tactile.c.U.getUID();
		this.cache[this.getCacheId(this.workon)] = this.workon;
		localStorage.setItem("ContentLoaderCached", JSON.stringify(this.cache));
		this.parent.load(this.workon);
	},
	unsetCache : function(config) {
		var cacheId = this.getCacheId(config);
		if (this.cache[cacheId] != null) {
			localStorage.removeItem("clc"+this.cache[cacheId].uid);
			delete this.cache[cacheId];
			localStorage.setItem("ContentLoaderCached", JSON.stringify(this.cache));
			this.statechange({ message: "cache removed", data: cacheId, method: "unsetCache" });
			return true;
		}
		if (!this.canCache)
			return true;
		this.statechange({ message: "cache unset fails, not found", data: cacheId, method: "unsetCache" });
		return false;
	},
	clearCache : function() {
		if (!this.canCache)
			return true;
		for (var i in this.cache)
			localStorage.removeItem("clc"+this.cache[i].uid);
		this.cache = {};
		localStorage.setItem("ContentLoaderCached", JSON.stringify(this.cache));
		this.statechange({ message: "cache purged" , data: null, method: "clearCache" });
	},
	load : function(config) {
		if (!this.canCache)
			return this.parent.load(config);
		this.workon = config;
		if (typeof(this.workon.url) == "undefined")
			throw new tactile.errors.InvalidArgument("tactile.ContentLoaderCached", "config.url");
		this.statechange({ message: "init", data: this.workon.url, method: "load" });
		if (!this.getCache())
			this.initCache();
	}
});
tactile.Layout = function(elem, config) {
	config = config || {};
	if (config.position && tactile.Enum.contains(tactile.Enum.Position, config.position)) {
		config.position = tactile.Enum.getValue(tactile.Enum.Position, config.position);
	} else {
		config.position = tactile.Enum.Position.absolute;
	}
	if (config.overflow && tactile.Enum.contains(tactile.Enum.Overflow, config.overflow)) {
		config.overflow = tactile.Enum.getValue(tactile.Enum.Overflow, config.overflow);
	} else {
		config.overflow = tactile.Enum.Overflow.hidden;
	}
	var el = (typeof elem == "string") ? elem : elem.id;
	if (config.parent) {
		this.parent = config.parent;
	} else {
		tactile.foundation.LayoutManager.addLayout(this);
	}
	if(config.template){
		if(config.height){
			var fnHeight = config.height;
			config.height = function(parent){
				if(this.validateVisible()){
					if(typeof fnHeight === "function"){
						return Math.round(fnHeight.call(this,parent));
					}
					return Math.round(fnHeight);
				}
				return 0;
			};
		}
		if(config.width){
			var fnWidth = config.width;
			config.width = function(parent){
				if(this.validateVisible()){
					if(typeof fnWidth === "function"){
						return Math.round(fnWidth.call(this,parent));
					}
					return Math.round(fnWidth);
				}
				return 0;
			};
		}
		if(config.top){
			var fnTop = config.top;
			config.top = function(parent){
				if(this.validateVisible()){
					if(typeof fnTop === "function"){
						return Math.round(fnTop.call(this,parent));
					}
					return Math.round(fnTop);
				}
				return 0;
			};
		}
		if(config.left){
			var fnLeft = config.left;
			config.left = function(parent){
				if(this.validateVisible()){
					if(typeof fnLeft === "function"){
						return Math.round(fnLeft.call(this,parent));
					}
					return Math.round(fnLeft);
				}
				return 0;
			};
		}
		if(config.template === "top" || config.template === "bottom"){
			config.height = config.height || function(parent){
				if(this.validateVisible()){
					return Math.round(parent.height*0.15);
				}
				return 0;
			};
			config.width = config.width || function(parent){
				if(this.validateVisible()){
					return parent.width;
				}
				return 0;
			};
			if(config.template === "top"){
				tactile.c.E.addClass(document.getElementById(el),this.parent.id+"_North");
				config.top = config.top || function(parent){return 0;};
			}
			if(config.template === "bottom"){
				tactile.c.E.addClass(document.getElementById(el),this.parent.id+"_South");
				config.top = config.top || function(parent){
					if(this.validateVisible()){
						var height = 0;
						var north = document.getElementsByClassName(this.parent.id+"_North");
						var center = document.getElementsByClassName(this.parent.id+"_Center");
						height = north[0] ? height + tactile.page.getComponent(north[0].id).configHeight(parent) : height;
						height = center[0] ? height + tactile.page.getComponent(center[0].id).configHeight(parent) : height;
						return height;
					}
					return 0;
				};
			}
		}
		if(config.template === "center"){
			tactile.c.E.addClass(document.getElementById(el),this.parent.id+"_Center");
			config.width = config.width || function(parent){
				if(this.validateVisible()){
					var width = parent.width;
					var east = document.getElementsByClassName(this.parent.id+"_East");
					var west = document.getElementsByClassName(this.parent.id+"_West");
					width = east[0] ? width - tactile.page.getComponent(east[0].id).configWidth(parent) : width;
					width = west[0] ? width - tactile.page.getComponent(west[0].id).configWidth(parent) : width;
					return width;
				}
				return 0;
			};
			config.height = config.height || function(parent){
				if(this.validateVisible()){
					var height = parent.height;
					var north = document.getElementsByClassName(this.parent.id+"_North");
					var south = document.getElementsByClassName(this.parent.id+"_South");
					height = north[0] ? height - tactile.page.getComponent(north[0].id).configHeight(parent) : height;
					height = south[0] ? height - tactile.page.getComponent(south[0].id).configHeight(parent) : height;
					return height;
				}
				return 0;
			};
			config.top = config.top || function(parent){
				var north = document.getElementsByClassName(this.parent.id+"_North");
				if(north){
					if(this.validateVisible()){
						return tactile.page.getComponent(north[0].id).configHeight(parent);
					}
				}
				return 0;
			};
			config.left = config.left || function(parent){
				if(this.validateVisible()){
					var west = document.getElementsByClassName(this.parent.id+"_West");
					var left = west[0] ? tactile.page.getComponent(west[0].id).configWidth(parent) : 0;
					return left;
				}
				return 0;
			};
		}
		if(config.template === "left"){
			tactile.c.E.addClass(document.getElementById(el),this.parent.id+"_West");
			config.width = config.width || function(parent){
				if(this.validateVisible()){
					return Math.round(parent.width*0.15);
				}
				return 0;
			};
			config.top = config.top || function(parent){
				var north = document.getElementsByClassName(this.parent.id+"_North");
				if(north){
					if(this.validateVisible()){
						return tactile.page.getComponent(north[0].id).configHeight(parent);
					}
				}
				return 0;
			};
			config.height = config.height || function(parent){
				if(this.validateVisible()){
					var height = parent.height;
					var north = document.getElementsByClassName(this.parent.id+"_North");
					var south = document.getElementsByClassName(this.parent.id+"_South");
					height = north[0] ? height - tactile.page.getComponent(north[0].id).configHeight(parent) : height;
					height = south[0] ? height - tactile.page.getComponent(south[0].id).configHeight(parent) : height;
					return height;
				}
				return 0;
			};
		}
		if(config.template === "right"){
			tactile.c.E.addClass(document.getElementById(el),this.parent.id+"_East");
			config.width = config.width || function(parent){
				if(this.validateVisible()){
					return Math.round(parent.width*0.15);
				}
				return 0;
			};
			config.top = config.top || function(parent){
				var north = document.getElementsByClassName(this.parent.id+"_North");
				if(north){
					if(this.validateVisible()){
						return tactile.page.getComponent(north[0].id).configHeight(parent);
					}
				}
				return 0;
			};
			config.height = config.height || function(parent){
				if(this.validateVisible()){
					var height = parent.height;
					var north = document.getElementsByClassName(this.parent.id+"_North");
					var south = document.getElementsByClassName(this.parent.id+"_South");
					height = north[0] ? height - tactile.page.getComponent(north[0].id).configHeight(parent) : height;
					height = south[0] ? height - tactile.page.getComponent(south[0].id).configHeight(parent) : height;
					return height;
				}
				return 0;
			};
			config.left = config.left || function(parent){
				if(this.validateVisible()){
					var west = document.getElementsByClassName(this.parent.id+"_West");
					var center = document.getElementsByClassName(this.parent.id+"_Center");
					var left = west[0] ? tactile.page.getComponent(west[0].id).configWidth(parent) : 0;
					left = center[0] ? left + tactile.page.getComponent(center[0].id).configWidth(parent) : left;
					return left;
				}
				return 0;
			};
		}
	}
	if(!config.width){
		throw new tactile.errors.ArgumentUndefined("tactile.Layout","config.width");
	}
	if(!config.height){
		throw new tactile.errors.ArgumentUndefined("tactile.Layout","config.height");
	}
	tactile.Layout.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	this.domchanged = new tactile.foundation.Observer();
	if (config.parent && config.parent.getBox ) {
		config.parent.onresize.subscribe(this.update, this);
	}
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.beforehide.subscribe(function(e) {this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	if(tactile.Device.platform === "ios" && tactile.Device.version >= 5){
		this.aftershow.subscribe(function(e) {tactile.c.U.updateRenderTreeFix(this.elem);}, this);
	}
};
tactile.Layout.cast = function(component){
	return (component instanceof tactile.Layout) ? component : null;
};
tactile.Layout.prototype = new tactile.foundation.UIContainer();
tactile.Layout.extend({
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	updateState: function(e) {
		this.onstatechange.fire(e);
	},
	append: function(container) {
		if (container instanceof tactile.Layout ||container instanceof tactile.View || container instanceof tactile.MultiView || container instanceof tactile.FlexView || component instanceof tactile.GridView ) {
			if (container.parent) {
				container.parent.onresize.unsubscribe(container.update);
			}
			tactile.foundation.UIContainer.prototype.append.call(this, container);
			container.parent.onresize.subscribe(container.update, container);
			this.domchanged.fire({});
		} else {
			throw new tactile.errors.TypeError("tactile.Layout.append","tactile.View, tactile.FlexView or tactile.MultiView",typeof container);
		}
	},
	prepend: function(container) {
		if (container instanceof tactile.Layout ||container instanceof tactile.View || container instanceof tactile.MultiView || container instanceof tactile.FlexView || component instanceof tactile.GridView ) {
			if (container.parent) {
				container.parent.onresize.unsubscribe(container.update);
			}
			tactile.foundation.UIContainer.prototype.prepend.call(this, container);
			container.parent.onresize.subscribe(container.update, container);
			this.domchanged.fire({});
		} else {
			throw new tactile.errors.TypeError("tactile.Layout.prepend","tactile.View, tactile.FlexView or tactile.MultiView",typeof container);
		}
	},
	remove: function(component) {
		if (component instanceof tactile.Layout || component instanceof tactile.View || component instanceof tactile.MultiView || component instanceof tactile.FlexView || component instanceof tactile.GridView ) {
			tactile.foundation.UIComponent.prototype.remove.call(this, component);
			this.domchanged.fire({});
		} else {
			throw new tactile.errors.TypeError("tactile.Layout.remove","tactile.View, tactile.FlexView or tactile.MultiView",typeof component);
		}
	},
	clone: function(contentCopy) {
		return tactile.foundation.UIContainer.prototype.clone(this, contentCopy);
	},
	toString: function() {
		return "Object - tactile.Layout";
	}
});
tactile.Layout.toString = function() {
	return "tactile.Layout";
};
tactile.View = function(elem, config) {
	config = config || {};
	if (config.parent) {
		if (config.parent instanceof tactile.foundation.UIContainer === true) {
			this.parent = config.parent;
		} else {
			tactile.logger.error("Error - type mismatch: View " + this.id + " expects parent of type UIContainer!");
		}
	}
	if(config.effect){
		if(config.effect.type == tactile.Enum.Effects.expand || config.effect.type == tactile.Enum.Effects.slide){
			if (!config.position) {
				config.position = tactile.Enum.Position.absolute;
			}
			if(config.effect.type == tactile.Enum.Effects.expand){
				if (!config.overflow) {
					config.overflow = tactile.Enum.Overflow.hidden;
				}
			}
			if (!config.width && this.parent){
				config.width = this.parent.configWidth;
			}
		}
	}
	tactile.View.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	if (this.parent && this.parent instanceof tactile.MultiView === true) {
		if (!this.parent.views.contains(this)) {
			this.parent.addView(this);
		}
	}
	if (this.parent && this.parent.getBox ) {
		this.parent.onresize.subscribe(this.update, this);
	}
	this.scroller = undefined;
	this.scrollConfig = undefined;
	this.scrollerInitialized = false;
	if (config.scroll) {
		var scrollconfig = {scroll: config.scroll};
		if (config.scrollbar) {
			scrollconfig.scrollbar = config.scrollbar;
		}
		if(typeof config.bounce === "boolean"){
			scrollconfig.bounce = config.bounce;
		}
		if(this.parent){
			this.scroller = new tactile.foundation.Scroller(this, scrollconfig);
			this.scrollerInitialized = true;
			tactile.logger.info(this.toString() + ", scroller: " + this.scroller.toString());
		}else{
			this.scrollConfig = scrollconfig;
		}
	}
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.beforehide.subscribe(function(e) {this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	if(tactile.Device.platform === "ios" && tactile.Device.version >= 5){
		this.aftershow.subscribe(function(e) {tactile.c.U.updateRenderTreeFix(this.elem);}, this);
	}
	tactile.EventManager.addListener(this.id, 'click', function(e){
        var hash = null, parent = e.target;
        do {
            if (parent.href) {
                if (parent.hash) {
                    hash = unescape(parent.hash);
                } else {
                    href = unescape(parent.href);
                }
                break;
            }
            parent = parent.parentNode;
        } while (parent);
        if (hash){
            e.browserEvent.preventDefault();
            try{
                tactile.c.U.attrValidator.validate(hash, true);
            } catch (e){
            	tactile.logger.log(e);
            }
            return false;
        }
    });
};
tactile.View.prototype = new tactile.foundation.UIContainer();
tactile.View.cast = function(component){
	return (component instanceof tactile.View) ? component : null;
};
tactile.View.extend({
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	updateState: function(e) {
		if ((this.state === "hiding" || this.state === "showing") && this.effect && this.effect.transitionEnd) {
			this.effect.transitionEnd({});
		}
		this.onstatechange.fire(e);
	},
	append: function(container) {
		if (container instanceof tactile.View || container instanceof tactile.Picture || container instanceof tactile.MultiView || container instanceof tactile.FlexView || container instanceof tactile.GridView) {
			if (container.parent) {
				container.parent.onresize.unsubscribe(container.update);
			}
			tactile.foundation.UIContainer.prototype.append.call(this, container);
			container.parent.onresize.subscribe(container.update, container);
		} else {
			throw new tactile.errors.TypeError("tactile.View.append","tactile.View, tactile.FlexView or tactile.MultiView",typeof container);
		}
	},
	prepend: function(container) {
		if (container instanceof tactile.View || container instanceof tactile.Picture || container instanceof tactile.MultiView || container instanceof tactile.FlexView || container instanceof tactile.GridView) {
			if (container.parent) {
				container.parent.onresize.unsubscribe(container.update);
			}
			tactile.foundation.UIContainer.prototype.prepend.call(this, container);
			container.parent.onresize.subscribe(container.update, container);
		} else {
			throw new tactile.errors.TypeError("tactile.View.prepend","tactile.View, tactile.FlexView or tactile.MultiView",typeof container);
		}
	},
	clone: function(contentCopy){
		return tactile.foundation.UIContainer.prototype.clone(this, contentCopy);
	},
	show: function(config) {
		if(this.scroller && this.scroller.scrollbar){
			this.scroller.scrollbar.hBar.style.display = "block";
			this.scroller.scrollbar.vBar.style.display = "block";
		}
		if (this.parent && this.parent instanceof tactile.MultiView && this.show.caller != tactile.MultiView.prototype.showView) {
			this.parent.showView(this.id, config);
		} else {
			tactile.foundation.UIComponent.prototype.show.call(this, config);
		}
	},
	hide: function(config) {
		if(this.scroller && this.scroller.scrollbar){
			this.scroller.scrollbar.hBar.style.display = "none";
			this.scroller.scrollbar.vBar.style.display = "none";
		}
		if (this.parent && this.parent instanceof tactile.MultiView && this.hide.caller != tactile.MultiView.prototype.showView) {
	        if (this.parent.views[this.parent.selectedIndex] == this) {
	            this.parent.showNext(config);
			}
		} else {
	        tactile.foundation.UIComponent.prototype.hide.call(this, config);
		}
	},
	setContent : function(value){
	    var element = this;
	    if(element instanceof tactile.View){
	        if(element.getChildren().length === 0){
	        	if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.View.prototype.setContent', value);
	            element.elem.getElementsByTagName('div')[0].innerHTML = value;
	        }
	    }else{
	        throw tactile.errors.TypeError("tactile.View.prototype.setContent","tactile.View", typeof element);
	    }
	},
	addContent : function(value){
		if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.View.prototype.addContent', value);
		this.setContent(this.elem.getElementsByTagName('div')[0].innerHTML + value);
	},
	toString: function() {
		return "Object - tactile.View";
	}
});
tactile.View.toString = function() {
	return "tactile.View";
};
tactile.MultiView = function(elem, config) {
	config = config || {};
	config.width = config.width || function(box) {return box.width;};
	config.height = config.height || function(box) {return box.height;};
	if (config.parent) {
		this.parent = config.parent;
	}
	tactile.MultiView.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	this.onswitch = new tactile.foundation.Observer();
	if (this.parent && this.parent.getBox ) {
		this.parent.onresize.subscribe(this.update, this);
	}
	this.views = new Array();
	this.selectedIndex = 0;
	this.prevIndex = 0;
	if (config.selectedIndex && !isNaN(config.selectedIndex)) {
		this.selectedIndex = config.selectedIndex;
	}
	var self = this;
	if(config.swipe){
		this.swipe = config.swipe;
		this.elem.me = self;
		tactile.events.TouchEventManager.addListener(this.elem, "swipe", self.onSwipe);
	}
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.showing = false;
	this.hiding = false;
	this.beforehide.subscribe(function(e) {this.hiding = true; this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.hiding = false; this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.showing = true; this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.showing = false; this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	var switching = 0;
	this.setSwitching = function(status) {
		if (status) {
			switching += 2;
		} else {
			switching -= 1;
		}
	};
	this.isSwitching = function() {
		return (this.showing || this.hiding);
	};
};
tactile.MultiView.cast = function(component){
	return (component instanceof tactile.MultiView) ? component : null;
};
tactile.MultiView.prototype = new tactile.foundation.UIContainer();
tactile.MultiView.extend({
	addView: function(view, prepend) {
		if (view instanceof tactile.View) {
			if (!this.views.contains(view)) {
				if (this.selectedIndex  != this.views.length) {
					view.elem.style.display = tactile.Enum.Display.none;
				}
				view.afterhide.subscribe(function() {this.setSwitching(false);}, this);
				view.aftershow.subscribe(function() {this.setSwitching(false);}, this);
				if (! view.parent) {
					view.parent = this;
				}
				view.getVisibility = undefined;
				var self = this;
				view.afterhide.subscribe(function(e){
					self.afterhide.fire({index:self.prevIndex});
				});
				view.aftershow.subscribe(function(e){
					self.aftershow.fire({index:self.selectedIndex});
				});
				if(!prepend){
					this.views.push(view);
				}else{
					this.views.unshift(view);
				}
			}
			return view;
		} else {
			throw new tactile.errors.TypeError("tactile.MultiView.addView","type tactile.View",typeof view);
		}
	},
	addViewById: function(id, config) {
		config = config || {};
		config.parent = config.parent || this;
		return this.addView(new tactile.View(id, config));
	},
	addViewsByClassName: function(classname, config) {
		config = config || {};
		config.parent = config.parent || this;
		var views = tactile.c.E.getElementsByClassName(this.elem,classname);
		for (var i = 0; i < views.length; i++) {
			this.addView(new tactile.View(views[i], config));
		}
	},
	getViews: function() {
	    return this.views;
	},
	getViewByIndex: function(index){
	    if(index < this.views.length && index >= 0 && this.views[index]){
	        return this.views[index];
	    }
	    return null;
	},
	append: function(view) {
		if (view instanceof tactile.View) {
			if (view.parent) {
				view.parent.onresize.unsubscribe(view.update);
			}
			tactile.foundation.UIContainer.prototype.append.call(this, view);
			view.parent.onresize.subscribe(view.update, view);
			this.addView(view);
		} else {
			throw new tactile.errors.TypeError("tactile.MultiView.appendView","tactile.View",typeof view);
		}
	},
	prepend: function(view) {
		if (view instanceof tactile.View) {
			if (view.parent) {
				view.parent.onresize.unsubscribe(view.update);
			}
			tactile.foundation.UIContainer.prototype.prepend.call(this, view);
			view.parent.onresize.subscribe(view.update, view);
			this.addView(view, true);
		} else {
			throw new tactile.errors.TypeError("tactile.MultiView.appendView","tactile.View",typeof view);
		}
	},
	remove: function(view) {
		if (view instanceof tactile.View) {
			if (this.views.contains(view) && this.views.length > 1) {
				var idx = this.views.indexOf(view, 0);
				if (idx != -1) {
					if(this.selectedIndex == idx){
						var self = this;
						this.showNext();
						if(view.effect){
							duration = view.effect.duration;
						}
						if(view.effect){
							view.afterhide.subscribe(function(e) {
								tactile.foundation.UIComponent.prototype.remove.call(self, view);
								self.views.splice(idx,1);
								if(self.selectedIndex > 0){
									self.selectedIndex -= 1;
								}
							}, this);
						}else{
							tactile.foundation.UIComponent.prototype.remove.call(self, view);
							self.views.splice(idx,1);
							if(self.selectedIndex > 0){
								self.selectedIndex -= 1;
							}
						}
					}else{
						tactile.foundation.UIComponent.prototype.remove.call(this, view);
						this.views.splice(idx,1);
						if(this.selectedIndex > idx){
							this.selectedIndex -= 1;
						}
					}
				}
			}
		}
	},
	inverseConfig: function(config) {
		var ret = {};
		if(typeof config === "object" && config.slideto && config.slidefrom){
			ret.slideto = config.slidefrom;
			ret.slidefrom = config.slideto;
		}
		return ret;
	},
	showView: function(index, config) {
		if(index < 0 || index >= this.views.length) {
			tactile.logger.error(this.id + ".showView(" + index + ", " + config + "): Index out of bounds (0 - "+this.views.length+")");
			return;
		}
		if (this.isSwitching()) {
			return;
		}
		var prevIndex = null;
		if(!isNaN(index)){
			if(index != this.selectedIndex){
				if(index >= this.views.length){
					return;
				}
				prevIndex = this.selectedIndex;
				this.prevIndex = prevIndex;
				this.selectedIndex = index;
				this.setSwitching(true);
				this.beforehide.fire({index:prevIndex});
				this.views[prevIndex].hide(config);
				if(!this.views[prevIndex].effect){
					this.afterhide.fire({index:prevIndex});
				}
				var self = this;
				this.beforeshow.fire({index:index});
				this.views[this.selectedIndex].show(config);
				if(!this.views[this.selectedIndex].effect){
					this.aftershow.fire({index:self.selectedIndex});
				}
			}
		} else {
			var idx = null;
			for (var i = 0; i < this.views.length; i++) {
				if (this.views[i].id === index) {
					idx = i;
					break;
				}
			}
			if (idx !== null && idx != this.selectedIndex) {
				prevIndex = this.selectedIndex;
				this.prevIndex = prevIndex;
				this.selectedIndex = idx;
				this.setSwitching(true);
				var self = this;
				this.beforehide.fire({index:prevIndex});
				this.views[prevIndex].hide(config);
				if(!this.views[prevIndex].effect){
					this.afterhide.fire({index:prevIndex});
				}
				this.beforeshow.fire({index:index});
				this.views[this.selectedIndex].show(config);
				if(!this.views[this.selectedIndex].effect){
					this.aftershow.fire({index:self.selectedIndex});
				}
			}
		}
		if (prevIndex !== null) {
			var o = {
					target: this.id,
					previousIndex: prevIndex,
					selectedIndex: this.selectedIndex,
					previousId: this.views[prevIndex].id,
					selectedId: this.views[this.selectedIndex].id
					};
			this.onswitch.fire(o);
			var newbox = this.getInnerBox();
			this.onresize.fire(newbox);
		}
	},
	showNext: function(config) {
		if (this.selectedIndex + 1 >= this.views.length) {
			this.showFirst(config);
		} else {
			this.showView(this.selectedIndex+1, config);
		}
	},
	showPrev: function(config) {
		if (this.selectedIndex-1 < 0) {
			this.showLast(config);
		} else {
			this.showView(this.selectedIndex-1, config);
		}
	},
	showFirst: function(config) {
		this.showView(0, config);
	},
	showLast: function(config) {
		this.showView(this.views.length-1, config);
	},
	update: function(box) {
		if(this.selectedIndex <0 || isNaN(this.selectedIndex)){
			this.selectedIndex = 0;
		}else if(this.selectedIndex > this.views.length - 1){
			this.selectedIndex = (this.views.length > 0) ? this.views.length - 1 : 0;
		}
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	updateState: function(e) {
		this.onstatechange.fire(e);
	},
	onSwipe: function(e) {
		var self = this.me;
		var direction = e.direction.toUpperCase();
		var swipeDir = self.validateSwipeDirection();
			if(direction == "LEFT"){
				self.showNext({slideto: tactile.Enum.Direction.west, slidefrom: tactile.Enum.Direction.east});
			}else if(direction == "RIGHT"){
				self.showPrev({slideto: tactile.Enum.Direction.east, slidefrom: tactile.Enum.Direction.west});
			}
	},
	validateSwipeDirection: function() {
		var res = tactile.effects.Direction.horizontal;
		if(this.swipe){
			if(typeof this.swipe === "function"){
				try{
					res = this.swipe();
				}catch(ex){
					res = tactile.effects.Direction.horizontal;
					tactile.logger.error(this.id + '.validateSwipeDirection() has thrown an exception: ' + ex);
				}
			}else if(typeof this.swipe === "string"){
				if(tactile.Enum.contains(tactile.effects.Direction, this.swipe)){
					res = this.swipe;
				}else{
					res = tactile.effects.Direction.horizontal;
					tactile.logger.error( this.id + ".validateSwipeDirection - tactile.effects.Direction doesn't contains " + this.swipe);
				}
			}
		}
		return res;
	},
	clone: function() {
		return tactile.foundation.UIContainer.prototype.clone(this, false);
	},
	toString: function() {
		return "Object - tactile.MultiView";
	}
});
tactile.MultiView.toString = function() {
	return "tactile.MultiView";
};
tactile.FlexView = function(elem, config) {
	config = config || {};
	if (config.parent) {
		if (config.parent instanceof tactile.foundation.UIContainer === true) {
			this.parent = config.parent;
		} else {
			tactile.logger.error("Error - type mismatch: tactile.Flexview " + this.id + " expects parent of type UIContainer!");
		}
	}
	tactile.FlexView.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	if (this.parent) {
		this.parent.onresize.subscribe(this.update, this);
	}
	this.flexitems = new Array();
	this.scroller = undefined;
	if (config.scroll) {
		var scrollconfig = {scroll: config.scroll};
		if (config.scrollbar) {
			scrollconfig.scrollbar = config.scrollbar;
		}
		if(typeof config.bounce === "boolean"){
			scrollconfig.bounce = config.bounce;
		}
		this.scroller = new tactile.foundation.Scroller(this, scrollconfig);
	}
	config.flexitem = config.flexitem || {};
	this.configFlexFlow = undefined;
	this.configFlexDimension = undefined;
	this.configFlexItemGap = undefined;
	this.configFlexItemWidth = undefined;
	this.configFlexItemHeight = undefined;
	this.configFlexItemMinWidth = undefined;
	this.configFlexItemMinHeight = undefined;
	if (config.flexflow) {
		if (typeof config.flexflow === "function") {
			this.configFlexFlow = config.flexflow;
		} else {
			if (tactile.Enum.contains(tactile.Enum.FlexFlow, config.flexflow)) {
				this.configFlexFlow =  function() {return config.flexflow;};
			} else {
				this.configFlexFlow =  function() {return tactile.Enum.FlexFlow.vertical;};
			}
		}
	} else {
		this.configFlexFlow =  function() {return tactile.Enum.FlexFlow.vertical;};
	}
	if (config.flexdimension) {
		if (typeof config.flexdimension === "function") {
			this.configFlexDimension = config.flexdimension;
		} else {
			if (tactile.Enum.contains(tactile.Enum.Dimension, config.flexdimension)) {
				this.configFlexDimension = function() {return config.flexdimension;};
			}
		}
	}
	if (config.flexitem.gap) {
		if (typeof config.flexitem.gap === "function") {
			this.configFlexItemGap = config.flexitem.gap;
		} else if (!isNaN(config.flexitem.gap)) {
			this.configFlexItemGap = function(box) {return config.flexitem.gap;};
		}
	} else {
		this.configFlexItemGap = function(box) {return 0;};
	}
	if (config.flexitem.width) {
		if (typeof config.flexitem.width === "function") {
			this.configFlexItemWidth = config.flexitem.width;
		} else if (!isNaN(config.flexitem.width)) {
			this.configFlexItemWidth = function(box) { return config.flexitem.width;};
		}
	}
	if (config.flexitem.height) {
		if (typeof config.flexitem.height === "function") {
			this.configFlexItemHeight = config.flexitem.height;
		} else if (!isNaN(config.flexitem.height)) {
			this.configFlexItemHeight = function(box) {return config.flexitem.height;};
		}
	}
	if (config.flexitem.minwidth) {
		if (typeof config.flexitem.minwidth === "function") {
			this.configFlexItemMinWidth = config.flexitem.minwidth;
		} else if (!isNaN(config.flexitem.minwidth)) {
			this.configFlexItemMinWidth = function(box) {return config.flexitem.minwidth;};
		}
	}
	if (config.flexitem.minheight) {
		if (typeof config.flexitem.minheight === "function") {
			this.configFlexItemMinHeight = config.flexitem.minheight;
		} else if (!isNaN(config.flexitem.minheight)) {
			this.configFlexItemMinHeight = function(box) {return config.flexitem.minheight;};
		}
	}
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.beforehide.subscribe(function(e) {this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
};
tactile.FlexView.cast = function(component){
	return (component instanceof tactile.FlexView) ? component : null;
};
tactile.FlexView.prototype = new tactile.foundation.UIContainer();
tactile.FlexView.extend({
	addItem: function(flexitem, prepend) {
		if (flexitem instanceof tactile.FlexItem ) {
			if (!this.flexitems.contains(flexitem)) {
				if(!prepend){
					this.flexitems.push(flexitem);
				}else{
					this.flexitems.unshift(flexitem);
				}
			}
			return flexitem;
		} else {
			throw new tactile.errors.TypeError("tactile.FlexView.addItem","tactile.FlexItem",typeof flexitem);
		}
	},
	addItemById: function(id, config) {
		config = config || {};
		config.parent = config.parent || this;
		return this.addItem(new tactile.FlexItem(id, config));
	},
	addItemsByClassName: function(classname, config) {
		config = config || {};
		config.parent = config.parent || this;
		var items = tactile.c.E.getElementsByClassName(this.elem,classname);
		for (var i = 0; i < items.length; i++) {
			this.addItem(new tactile.FlexItem(items[i], config));
		}
	},
	append: function(flexitem) {
		if (flexitem instanceof tactile.FlexItem) {
			tactile.foundation.UIContainer.prototype.append.call(this, flexitem);
			this.addItem(flexitem);
			this.update(this.getParentInnerBox());
		} else {
			throw new tactile.errors.TypeError("Error - type mismatch: tactile.FlexView.appendView expects argument of type tactile.FlexItem!");
		}
	},
	prepend: function(flexitem) {
		if (flexitem instanceof tactile.FlexItem) {
			tactile.foundation.UIContainer.prototype.prepend.call(this, flexitem);
			this.addItem(flexitem, true);
			this.update(this.getParentInnerBox());
		} else {
			throw new tactile.errors.TypeError("tactile.FlexView.prependView","tactile.FlexItem",typeof flexitem);
		}
	},
	getItems: function() {
	    return this.flexitems;
	},
	getItemByIndex: function(index){
		if(index < this.flexitems.length && index >= 0 && this.flexitems[index]){
	        return this.flexitems[index];
	    }
	    return null;
	},
	remove: function(flexitem) {
		if (flexitem instanceof tactile.FlexItem) {
			tactile.foundation.UIComponent.prototype.remove.call(this, flexitem);
			if (this.flexitems.contains(flexitem)) {
				var idx = this.flexitems.indexOf(flexitem, 0);
				if (idx != -1) {
					this.flexitems.splice(idx, 1);
				}
				this.update(this.getParentInnerBox());
			}
		}
	},
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var fb =  this.flexitems;
		var gap = this.configFlexItemGap(this.getBox());
		var toppos = gap;
		var leftpos = gap;
		var flexItemMinWidth = undefined;
		var flexItemMinHeight = undefined;
		var flexItemWidth = undefined;
		var flexItemHeight = undefined;
		var flexViewWidth = undefined;
		var flexViewHeight = undefined;
		var h = this.validateNumber(this.configHeight, this.getBox());
	    var w =  this.validateNumber(this.configWidth, this.getBox());
		if (this.configWidth != undefined && w != null) {
			flexViewWidth = w;
		} else {
			flexViewWidth = this.parent.getOuterBox().width - gap - gap;
		}
		if (this.configHeight != undefined && h != null) {
			flexViewHeight = h;
		} else {
			flexViewHeight = this.parent.getOuterBox().height - gap -gap;
		}
		if (this.configFlexItemWidth != undefined) {
			flexItemWidth = this.validateNumber(this.configFlexItemWidth, this.getBox());
			if (flexItemWidth >= flexViewWidth) {
				flexItemWidth = flexItemWidth - gap - gap;
			}
		} else {
			flexItemWidth = flexViewWidth;
		}
		if (this.configFlexItemHeight != undefined) {
			flexItemHeight = this.validateNumber(this.configFlexItemHeight, this.getBox());
			if (flexItemHeight >= flexViewHeight) {
				flexItemHeight = flexItemHeight - gap - gap;
			}
		} else {
			flexItemHeight = flexViewHeight - gap - gap;
		}
		if (this.configFlexItemMinWidth != undefined){
			flexItemMinWidth = this.validateNumber(this.configFlexItemMinWidth, this.getBox());
		}
		if (this.configFlexItemMinHeight != undefined){
			flexItemMinHeight = this.validateNumber(this.configFlexItemMinHeight, this.getBox());
		}
		if (flexItemHeight != undefined && flexItemMinHeight != undefined && flexItemHeight <= flexItemMinHeight) {
			flexItemHeight = flexItemMinHeight;
		}
		if (flexItemWidth != undefined && flexItemMinWidth != undefined && flexItemWidth <= flexItemMinWidth) {
			flexItemWidth = flexItemMinWidth;
		}
		for (var i = 0; i < fb.length; i++) {
			fb[i].resize({});
			if (this.validFlexFlow() == tactile.Enum.FlexFlow.vertical) {
				if ( this.configFlexItemHeight === undefined ) {
					if (( flexItemMinHeight !== undefined ) && ( flexItemMinHeight >= fb[i].getOuterBox().height )) {
						flexItemHeight = flexItemMinHeight;
					} else {
						flexItemHeight = fb[i].getOuterBox().height;
					}
				}
			} else {
				if (this.configFlexItemWidth === undefined){
					 if (( flexItemMinWidth !== undefined ) && ( flexItemMinWidth >= fb[i].getInnerBox().width )) {
						 flexItemWidth = flexItemMinWidth;
					 } else {
						 flexItemWidth = fb[i].getOuterBox().width;
					 }
				}
			}
			fb[i].resize({
				width: flexItemWidth,
				height: flexItemHeight,
				left: leftpos,
				top: toppos
			});
			fb[i].onresize.fire(fb[i].getInnerBox());
			if (this.configFlexFlow() == tactile.Enum.FlexFlow.vertical) {
				toppos += fb[i].getOuterBox().height + gap;
			} else {
				leftpos += fb[i].getOuterBox().width + gap;
			}
		}
		var fbox;
		var offset = this.getBoxOffset();
		var ht = this.validateNumber(this.configHeight, box);
	    var wt =  this.validateNumber(this.configWidth, box);
		if (this.validFlexFlow() == tactile.Enum.FlexFlow.vertical) {
			fbox = {
					width: ((this.configWidth != undefined && wt != null) ? (wt - offset.width) : box.width),
					height: toppos,
					left: ((this.configLeft != undefined) ? (this.configLeft(box) - offset.left) : undefined),
					top: ((this.configTop != undefined) ? (this.configTop(box) - offset.width) : undefined)
			};
		} else {
			fbox = {
					width: leftpos,
					height: ((this.configHeight != undefined && ht != null) ? (ht - offset.height) : box.height),
					left: ((this.configLeft != undefined) ? (this.configLeft(box) - offset.left) : undefined),
					top: ((this.configTop != undefined) ? (this.configTop(box) - offset.width) : undefined)
			};
		}
		this.resize(fbox);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	validFlexFlow: function () {
		var ret = tactile.Enum.FlexFlow.vertical;
		if (this.configFlexFlow) {
			try {
				ret = this.configFlexFlow();
			} catch (ex) {
				tactile.logger.error(this.id + '.validFlexFlow() has thrown an exception: ' + ex);
				ret = tactile.Enum.FlexFlow.vertical;
			}
			if (tactile.Enum.contains(tactile.Enum.FlexFlow, ret)) {
				return ret;
			} else {
				tactile.logger.error( this.id + ".validFlexFlow - tactile.Enum.FlexFlow doesn't contains " + ret);
				ret = tactile.Enum.FlexFlow.vertical;
			}
		}
		return ret;
	},
	validFlexDimension: function () {
		var ret = undefined;
		if (this.configFlexDimension) {
			try {
				ret = this.configFlexDimension();
			} catch (ex) {
				tactile.logger.error(this.id + '.validFlexDimension() has thrown an exception: ' + ex);
				ret = undefined;
			}
			if (tactile.Enum.contains(tactile.Enum.Dimension, ret)) {
				return ret;
			} else {
				tactile.logger.error( this.id + ".validFlexDimension - tactile.Enum.Dimension doesn't contains " + ret);
				ret = undefined;
			}
		}
		return ret;
	},
	clone: function() {
		return tactile.foundation.UIContainer.prototype.clone(this, false);
	},
	toString: function() {
		return "Object - tactile.FlexView";
	}
});
tactile.FlexView.toString = function() {
	return "tactile.FlexView";
};
tactile.FlexItem = function(elem, config) {
	config = config || {};
	if (config.position && tactile.Enum.contains(tactile.Enum.Position, config.position)) {
		config.position = tactile.Enum.getValue(tactile.Enum.Position, config.position);
	} else {
		config.position = tactile.Enum.Position.absolute;
	}
	if (config.parent) {
		if (config.parent instanceof tactile.FlexView === true) {
			this.parent = config.parent;
			if (!this.parent.flexitems.contains(this)) {
				this.parent.addItem(this);
			}
		} else {
			tactile.logger.error("Error - type mismatch: FlexItem " + this.id + " expects parent of type FlexView!");
		}
	}
	tactile.FlexItem.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.beforehide.subscribe(function(e) {this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	tactile.EventManager.addListener(this.id, 'click', function(e){
        var hash = null, parent = e.target;
        do {
            if (parent.href) {
                if (parent.hash) {
                    hash = unescape(parent.hash);
                } else {
                    href = unescape(parent.href);
                }
                break;
            }
            parent = parent.parentNode;
        } while (parent);
        if (hash){
            e.browserEvent.preventDefault();
            try{
                tactile.c.U.attrValidator.validate(hash, true);
            } catch (e){
            	tactile.logger.log(e);
            }
            return false;
        }
    });
};
tactile.FlexItem.prototype = new tactile.foundation.UIContainer();
tactile.FlexItem.extend({
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	clone: function(contentCopy){
		return tactile.foundation.UIContainer.prototype.clone(this, contentCopy);
	},
	setContent : function(value){
	    var element = this;
	    if(element instanceof tactile.FlexItem){
	        if(element.getChildren().length === 0){
	        	if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.FlexItem.prototype.setContent', value);
				element.elem.innerHTML = value;
	        }
	    }else{
	        throw tactile.errors.TypeError("tactile.FlexItem.prototype.setContent","tactile.FlexItem",typeof element);
	    }
	},
	addContent : function(value){
		if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.FlexItem.prototype.addContent', value);
		this.setContent(this.elem.getElementsByTagName('div')[0].innerHTML + value);
	},
	toString: function() {
		return "Object - tactile.FlexItem";
	}
});
tactile.FlexItem.cast = function(component){
	return (component instanceof tactile.FlexItem) ? component : null;
};
tactile.FlexItem.toString = function() {
	return "tactile.FlexItem";
};
tactile.GridView = function(elem, config) {
	config = config || {};
	tactile.GridView.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	if (config.parent) {
		this.parent = config.parent;
	}
	if (config.parent) {
		config.parent.onresize.subscribe(this.update, this);
	}
	this.griditems = new Array();
	this.scroller = undefined;
	if (config.scroll) {
		var scrollconfig = {scroll: config.scroll};
		if (config.scrollbar) {
			scrollconfig.scrollbar = config.scrollbar;
		}
		if(typeof config.bounce === "boolean"){
			scrollconfig.bounce = config.bounce;
		}
		this.scroller = new tactile.foundation.Scroller(this, scrollconfig);
	}
    if (config.gridflow) {
        if (typeof config.gridflow === "function") {
            this.configGridFlow = config.gridflow;
        } else {
            if (tactile.Enum.contains(tactile.Enum.GridFlow, config.gridflow)) {
                this.configGridFlow =  function() {return config.gridflow;};
            } else {
                this.configGridFlow =  function() {return tactile.Enum.GridFlow.vertical;};
            }
        }
    } else {
        this.configGridFlow =  function() {return tactile.Enum.GridFlow.vertical;};
    }
    this.containerAlign = config.align || 'left';
    if(config.griditem){
        this.gridItemConfig = {};
        if (config.griditem.vgap) {
            if (typeof config.griditem.vgap === "function") {
                this.gridItemConfig.vgap = config.griditem.vgap;
            } else if (!isNaN(config.griditem.vgap)) {
                this.gridItemConfig.vgap = function(box) {return config.griditem.vgap;};
            }
        } else {
            this.gridItemConfig.vgap = function(box) {return 0;};
        }
        if (config.griditem.hgap) {
            if (typeof config.griditem.hgap === "function") {
                this.gridItemConfig.hgap = config.griditem.hgap;
            } else if (!isNaN(config.griditem.hgap)) {
                this.gridItemConfig.hgap = function(box) {return config.griditem.hgap;};
            }
        } else {
            this.gridItemConfig.hgap = function(box) {return 0;};
        }
        if(this.configGridFlow() === tactile.Enum.GridFlow.horizontal && !config.griditem.itemswidth) {
        	this.configGridFlow =  function() {return tactile.Enum.GridFlow.vertical;};
        	tactile.logger.warn( this.id + ".validGridFlow - vertical GridFlow can`t be seted without gridItem width");
        } else {
            this.gridItemConfig.width = config.griditem.itemswidth;
        }
        if(this.configGridFlow() === tactile.Enum.GridFlow.vertical && !config.griditem.itemsheight) {
        	this.configGridFlow =  function() {return tactile.Enum.GridFlow.horizontal;};
        	tactile.logger.warn( this.id + ".validGridFlow - horizontal GridFlow can`t be seted without gridItem height");
        } else {
            this.gridItemConfig.height = config.griditem.itemsheight;
        }
        if(!config.griditem.itemsheight && !config.griditem.itemswidth){
        	throw new tactile.errors.InvalidArgument("tactile.GridView","config.griditem");
        }
    } else {
    	throw new tactile.errors.InvalidArgument("tactile.GridView","config.griditem");
    }
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
};
tactile.GridView.prototype = new tactile.foundation.UIContainer;
tactile.GridView.cast = function(component){
	return (component instanceof tactile.GridView) ? component : null;
};
tactile.GridView.extend({
	setAlign: function(position){
		if(~"left|right|center".indexOf(position)){
		    this.containerAlign = position;
		    this.update(this.getParentInnerBox());
		}
	},
	addItem: function(item, prepend){
	    if (item instanceof tactile.GridItem ) {
	        if (!this.griditems.contains(item)) {
	        	if(!prepend){
	        		this.griditems.push(item);
	        	}else{
	        		this.griditems.unshift(item);
	        	}
	        }
	        return item;
	    } else {
	    	throw new tactile.errors.TypeError("tactile.GridView.addItem","tactile.GridItem",typeof item);
	    }
	},
	append: function(griditem) {
	    if (griditem instanceof tactile.GridItem) {
	        tactile.foundation.UIContainer.prototype.append.call(this, griditem);
	        this.addItem(griditem);
	        this.update(this.getParentInnerBox());
	    } else {
	    	throw new tactile.errors.TypeError("tactile.GridView.append","tactile.GridItem",typeof griditem);
	    }
	},
	prepend: function(griditem) {
	    if (griditem instanceof tactile.GridItem) {
	        tactile.foundation.UIContainer.prototype.prepend.call(this, griditem);
	        this.addItem(griditem, true);
	        this.update(this.getParentInnerBox());
	    } else {
	    	throw new tactile.errors.TypeError("tactile.GridView.prepend","tactile.GridItem",typeof griditem);
	    }
	},
	getItems: function() {
	    return this.griditems;
	},
	getItemByIndex: function(index){
	    if(index < this.griditems.length && index >= 0 && this.griditems[index]){
	        return this.griditems[index];
	    }
	    return null;
	},
	remove: function(griditem) {
		if (griditem instanceof tactile.GridItem) {
	        tactile.foundation.UIComponent.prototype.remove.call(this, griditem);
	        if (this.griditems.contains(griditem)) {
	            var idx = this.griditems.indexOf(griditem, 0);
	            if (idx != -1) {
	                this.griditems.splice(idx, 1);
	            }
	            this.update(this.getParentInnerBox());
	        }
	    }
	},
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
	    var gridItemWidth, gridItemHeight,
	    hgap = this.gridItemConfig.hgap(this.getBox()),
	    vgap = this.gridItemConfig.vgap(this.getBox());
	    if (this.gridItemConfig.width != undefined) {
	        gridItemWidth = this.validateNumber(this.gridItemConfig.width, this.getBox());
	    }
	    if (this.gridItemConfig.height != undefined) {
	        gridItemHeight = this.validateNumber(this.gridItemConfig.height, this.getBox());
	    }
	    var coordinate = {
	        hgap: hgap,
	        vgap: vgap,
	        itemPerLine : 0,
	        row : 0,
	        cell : 0,
	        leftpos : hgap,
	        toppos: vgap,
	        biggestHeight: gridItemHeight || 0,
	        biggestWidth: gridItemWidth || 0,
	        flow: true,
	        increaseSize: function(item){
	            if(this.flow) this.biggestHeight = item.getInnerBox().height > this.biggestHeight ? item.getInnerBox().height : this.biggestHeight;
	            else this.biggestWidth = item.getInnerBox().width > this.biggestWidth ? item.getInnerBox().width : this.biggestWidth;
	        },
	        getGridSizes: function(sizeObj, scope, itemWidth, itemHeight){
	            sizeObj = sizeObj || {};
	            var h = scope.validateNumber(scope.configHeight, scope.getBox());
	            var w =  scope.validateNumber(scope.configWidth, scope.getBox());
	            if(this.flow){
	                sizeObj.gridViewWidth = (scope.configWidth != undefined && w != null) ? w : (itemWidth + this.hgap) * this.itemPerLine + this.hgap,
	                sizeObj.gridViewHeight = (scope.configHeight != undefined && h != null) ? h : this.toppos + this.biggestHeight + this.vgap;
	            } else {
	                sizeObj.gridViewWidth =  (scope.configWidth != undefined && w != null) ? w : this.leftpos + this.biggestWidth + this.hgap,
	                sizeObj.gridViewHeight = (scope.configHeight != undefined && h != null) ? h : (itemHeight + this.vgap) * this.itemPerLine + this.vgap;
	            }
	            return sizeObj;
	        }
	    };
	    var h = this.validateNumber(this.configHeight, this.getBox());
	    var w =  this.validateNumber(this.configWidth, this.getBox());
	    if (this.validGridFlow() == tactile.Enum.GridFlow.horizontal) {
	        coordinate.itemPerLine = Math.floor((((this.configWidth != undefined && w != null) ? w : this.parent.getInnerBox().width) - coordinate.hgap)/(gridItemWidth + coordinate.hgap));
	    } else {
	        coordinate.itemPerLine =  Math.floor((((this.configHeight != undefined && h != null) ? h : this.parent.getInnerBox().height) - coordinate.vgap)/(gridItemHeight + coordinate.vgap));
	        coordinate.flow = false;
	    }
	    for (var i = 0, j = this.griditems.length; i < j; i++) {
	        this.getActualCoordinate(coordinate);
	        this.griditems[i].resize({
	            width: gridItemWidth,
	            height: gridItemHeight,
	            left: coordinate.leftpos,
	            top: coordinate.toppos
	        });
	        coordinate.increaseSize(this.griditems[i]);
	    }
	    var gridSizes = {};
	    coordinate.getGridSizes(gridSizes, this, gridItemWidth, gridItemHeight);
	    for (var i = 0, j = this.griditems.length; i < j; i++) {
	        this.griditems[i].resize({
	            width: this.griditems[i].getInnerBox().width,
	            height: this.griditems[i].getInnerBox().height,
	            left: this.griditems[i].getBox().left,
	            top: this.griditems[i].getBox().top
	        });
	        this.griditems[i].onresize.fire(this.griditems[i].getInnerBox());
	    }
	    var fbox = {
	            width: gridSizes.gridViewWidth,
	            height: gridSizes.gridViewHeight,
	            left: this.containerAlign == "center" ?
	                (this.parent.getBox().width/2 - gridSizes.gridViewWidth/2) :
	                ( this.containerAlign == "right" ?
	                    (this.parent.getBox().width - gridSizes.gridViewWidth) :
	                    (((this.configLeft != undefined) ? this.validateNumber(this.configLeft, this.getBox()) : 0))),
	            top: ((this.configTop != undefined) ? this.validateNumber(this.configTop, this.getBox()) : 0)
	        };
	    this.resize(fbox);
	    var newbox = this.getInnerBox();
	    this.onresize.fire(newbox);
	},
	getActualCoordinate: function(coordinateObj){
	    coordinateObj.leftpos = coordinateObj.flow ? (coordinateObj.biggestWidth + coordinateObj.hgap) * coordinateObj.cell + coordinateObj.hgap : coordinateObj.leftpos;
	    coordinateObj.toppos = !coordinateObj.flow ? (coordinateObj.biggestHeight + coordinateObj.vgap) * coordinateObj.cell + coordinateObj.vgap : coordinateObj.toppos;
	    if(coordinateObj.cell > 0 && (coordinateObj.cell % coordinateObj.itemPerLine == 0)){
	        coordinateObj.leftpos = coordinateObj.flow ? coordinateObj.hgap : coordinateObj.leftpos + coordinateObj.biggestWidth + coordinateObj.hgap;
	        coordinateObj.toppos = coordinateObj.flow ? coordinateObj.toppos + coordinateObj.biggestHeight + coordinateObj.vgap : coordinateObj.vgap;
	        if(coordinateObj.flow) coordinateObj.biggestHeight = 0;
	        else coordinateObj.biggestWidth = 0;
	        coordinateObj.row += 1;
	        coordinateObj.cell = 1;
	    } else {
	        coordinateObj.cell += 1;
	    }
	},
	validGridFlow: function () {
	    var ret = tactile.Enum.GridFlow.vertical;
	    if (this.configGridFlow){
	        try {
	            ret = this.configGridFlow();
	        } catch (ex) {
	            tactile.logger.error(this.id + '.validGridFlow() has thrown an exception: ' + ex);
	            ret = tactile.Enum.GridFlow.vertical;
	        }
	        if (tactile.Enum.contains(tactile.Enum.GridFlow, ret)) {
	        	if(ret === tactile.Enum.GridFlow.horizontal && !this.gridItemConfig.width) {
	            	ret = tactile.Enum.GridFlow.vertical;
	            	tactile.logger.warn( this.id + ".validGridFlow - horizontal GridFlow can`t be seted without gridItem width");
	        	} else if(ret === tactile.Enum.GridFlow.vertical && !this.gridItemConfig.height){
	        		ret = tactile.Enum.GridFlow.horizontal;
	        		tactile.logger.warn( this.id + ".validGridFlow - vertical GridFlow can`t be seted without gridItem height");
	        	}
	        	return ret;
	        } else {
	            tactile.logger.error( this.id + ".validGridFlow - tactile.Enum.GridFlow doesn't contains " + ret);
	            ret = tactile.Enum.GridFlow.horizontal;
	        }
	    }
	    return ret;
	},
	toString: function() {
		return "Object - tactile.GridView";
	}
});
tactile.GridView.toString = function() {
	return "tactile.GridView";
};
tactile.GridItem = function(elem, config) {
	config = config || {};
	if (config.position && tactile.Enum.contains(tactile.Enum.Position, config.position)) {
		config.position = tactile.Enum.getValue(tactile.Enum.Position, config.position);
	} else {
		config.position = tactile.Enum.Position.absolute;
	}
	if (config.parent) {
		if (config.parent instanceof tactile.GridView === true) {
			this.parent = config.parent;
			if (!this.parent.griditems.contains(this)) {
				this.parent.addItem(this);
			}
		} else {
			tactile.logger.error("Error - type mismatch: GridItem " + this.id + " expects parent of type GridView!");
		}
	}
	tactile.GridItem.prototype.init.call(this, elem, config);
	this.onresize = new tactile.foundation.Observer();
	this.onstatechange = new tactile.foundation.Observer();
	this.aftershow = new tactile.foundation.Observer();
	this.beforeshow = new tactile.foundation.Observer();
	this.afterhide = new tactile.foundation.Observer();
	this.beforehide = new tactile.foundation.Observer();
	this.aftershow.subscribe(function(e) {this.update(this.getParentInnerBox());}, this);
	this.state = "initialized";
	this.beforehide.subscribe(function(e) {this.state = "hiding";}, this);
	this.afterhide.subscribe(function(e) {this.state = "hidden";}, this);
	this.afterhide.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	this.beforeshow.subscribe(function(e) {this.state = "showing";}, this);
	this.aftershow.subscribe(function(e) {this.state = "showed";}, this);
	this.aftershow.subscribe(function(e) {this.onstatechange.fire({sourceId: e.targetId});}, this);
	tactile.EventManager.addListener(this.id, 'click', function(e){
        var hash = null, parent = e.target;
        do {
            if (parent.href) {
                if (parent.hash) {
                    hash = unescape(parent.hash);
                } else {
                    href = unescape(parent.href);
                }
                break;
            }
            parent = parent.parentNode;
        } while (parent);
        if (hash){
            e.browserEvent.preventDefault();
            try{
                tactile.c.U.attrValidator.validate(hash, true);
            } catch (e){
            	tactile.logger.log(e);
            }
            return false;
        }
    });
};
tactile.GridItem.prototype = new tactile.foundation.UIContainer();
tactile.GridItem.extend({
	update: function(box) {
		tactile.foundation.UIContainer.prototype.update.call(this, box);
		var newbox = this.getInnerBox();
		this.onresize.fire(newbox);
	},
	clone: function(contentCopy){
		return tactile.foundation.UIContainer.prototype.clone(this, contentCopy);
	},
	setContent : function(value){
	    var element = this;
	    if(element instanceof tactile.GridItem){
	        if(element.getChildren().length === 0){
	        	if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.GridItem.prototype.setContent', value);
				element.elem.innerHTML = value;
	        }
	    }else{
	        throw tactile.errors.TypeError("tactile.GridItem.prototype.setContent","tactile.GridItem", typeof element);
	    }
	},
	addContent : function(value){
		if(typeof value !== 'string') throw tactile.errors.InvalidArgument('tactile.GridItem.prototype.addContent', value);
		this.setContent(this.elem.getElementsByTagName('div')[0].innerHTML + value);
	},
	toString: function() {
		return "Object - tactile.GridItem";
	}
});
tactile.GridItem.toString = function() {
	return "tactile.GridItem";
};
tactile.GridItem.cast = function(component){
	return (component instanceof tactile.GridItem) ? component : null;
};
tactile.EventManager = {
    addResizeListener: function(fn, scope) {
        if (!fn) {
        	throw new tactile.errors.ArgumentUndefined('tactile.EventManager.addResizeListener','fn');
        }
    	tactile.c.V.addResizeListener(fn, scope);
    },
    addDomReadyListener: function(fn) {
        if (!fn) {
        	throw new tactile.errors.ArgumentUndefined('tactile.EventManager.addDomReadyListene','fn');
        }
    	tactile.c.V.domReady.add(fn);
    },
    addOnloadListener: function(fn) {
    	tactile.c.V.addListener(window, "load", fn);
    },
    addUnloadListener: function(fn) {
    	tactile.c.V.addListener(window, "unload", fn);
    },
	addListener: function(el, ev, fn) {
		if (!fn) {
        	throw new tactile.errors.ArgumentUndefined("tactile.EventManager.addListener","fn");
        }
		var elem = null;
		if(typeof el === "string"){
			elem = document.getElementById(el);
		}else if(tactile.c.E.isHTMLElement(el)){
			elem = el;
		}else if(el instanceof tactile.foundation.Element){
			elem = el.elem;
		}
        if (!elem) {
            tactile.logger.error("tactile.EventManager.addListener - No element found to add event " + ev + " for element: "+ el);
            return;
        }
        if(elem.id === ""){
        	elem.setAttribute("id",tactile.c.U.getUID());
        }
        if (tactile.events && tactile.events.TouchEventManager.isTouchEvent(ev)) {
        	tactile.events.TouchEventManager.addListener(elem, ev, fn);
        	return true;
        } else {
        	return tactile.c.V.addManagedListener(elem, ev, fn);
        }
	},
	addSecuredListener: function(id, ev, fn) {
		var securedfn = function(e) { try {fn.call(e);} catch (ex) { tactile.logger.error("tactile.EventManager.addSecuredListener - " + ex); }};
		tactile.EventManager.addListener(id, ev, securedfn);
	},
	removeListener: function(id, ev, fn) {
		var elem;
		if(!ev && !fn){
			if(id in tactile.c.V.listeners){
				elem = id;
				ev = tactile.c.V.listeners[id].event;
	        	fn = undefined;
			}
        }else{
        	if(typeof id === "string"){
        		elem = document.getElementById(id);
        	}else if(id instanceof tactile.foundation.Element){
        		elem = id.elem;
        	}
        }
		if(!fn && !(id in tactile.c.V.listeners)){
			throw new tactile.errors.ArgumentUndefined("tactile.EventManager.removeListener","fn");
	    }
		if (!elem) {
        	tactile.logger.error("tactile.EventManager.removeListener - No element found to remove event " + ev + " for id: "+ id);
        	return;
        }
		if (tactile.events && tactile.events.TouchEventManager.isTouchEvent(ev) > 0) {
        	tactile.events.TouchEventManager.removeListener(elem, ev, fn);
        } else {
        	tactile.c.V.removeManagedListener(elem, ev, fn);
        }
	}
};
tactile.page = (function() {
	var Page = function() {
		var _onready = new tactile.foundation.Observer();
		this.onready = function(fn) {
			_onready.subscribe(fn);
		};
		this.inits = [];
		var me = this;
		function fireInits() {
			 me.inits.forEach(
	            function(el) {
	                el.call(window);
	            }
			  );
			 me.init = function(constructor) {
				 constructor.call(window);
			 };
		}
		tactile.EventManager.addDomReadyListener(function() {
			fireInits();
			tactile.c.D.screen.updateOrientation();
			_onready.fire();
			tactile.foundation.LayoutManager.init();
		});
		tactile.EventManager.addResizeListener(tactile.c.D.screen.updateOrientation);
	};
	Page.extend({
		getComponent: function(key) {
			if (tactile.foundation.UIComponent.components[key]) {
				return tactile.foundation.UIComponent.components[key];
			} else {
				var el = document.getElementById(key);
				if (el) {
					return new tactile.foundation.UIElement(el);
				} else {
					return null;
				}
			}
		},
		isComponent: function(key) {
			if (tactile.foundation.UIComponent.components[key]) {
				return true;
			} else {
				return false;
			}
		},
		init: function(constructor) {
			this.inits.push(constructor);
		},
		add: function(layout){
			if(layout instanceof tactile.Layout){
				var bodyContainer = document.getFirstBodyChild();
				if(bodyContainer && layout.elem){
					bodyContainer.appendChild(layout.elem);
					tactile.foundation.LayoutManager.updateLayout();
				}
			}else{
				throw new tactile.errors.TypeError("tactile.page.add","tactile.Layout",typeof layout);
			}
		},
		remove: function(layout){
			if(layout instanceof tactile.Layout){
				if(layout.elem){
					layout.elem.parentNode.removeChild(layout.elem);
					if(tactile.foundation.UIComponent.components[layout.id]){
						delete tactile.foundation.UIComponent.components[layout.id];
						tactile.foundation.LayoutManager.updateLayout();
					}
				}
			}else{
				throw new tactile.errors.TypeError("tactile.page.remove","tactile.Layout",typeof layout);
			}
		}
	});
	return new Page();
})();
if (tactile.Device.platform === "ios") {
	window.addEventListener("load",function(){
		document.addEventListener("touchmove",function(e){tactile.page.isClick = false;},false);
		tactile.c.U.fixLinks();
	},false);
}